New FAMILUG

The PyMiers

Saturday, 25 February 2017

Diễn đàn thảo luận Python, Django

Bạn cần giải đáp thắc mắc về Python, Django 🤔?
Vô ngay



Các chuyên gia chém gió đang chờ đợi để mổ xẻ câu hỏi của bạn 😎

Kênh cung cấp cả phòng chém gió về hệ thống Linux, DevOps, Docker hay kể cả Golang

Wednesday, 15 February 2017

File /etc/resolv.conf chỉ chứa được 3 nameserver

File /etc/resolv.conf trên các hệ điều hành dùng nhân Linux dùng để chứa thông tin về DNS server mà máy sẽ sử dụng.

Ví dụ muốn dùng Public DNS server của Google thì viết vào file này nội dung:
nameserver 8.8.8.8
nameserver 8.8.4.4 # thêm dòng nữa cho chắc, nhỡ cái đầu toi ta dùng cái 2 😎
nameserver 208.67.222.222 # đây là OpenDNS server
nameserver 9.6.6.9 # dòng này không có tác dụng, chỉ 3 dòng nameserver đầu tiên có tác dụng.

Đây là bug được report từ năm 2005 nhưng tới nay vẫn chưa được giải quyết, xem tại RedHat

Đây là nội dung file /etc/resolv.conf của 1 server bình thường:
root@81023a4b3012:/# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 8.8.8.8
nameserver 8.8.4.4
Chú ý là nameserver chứ không phải servername.
như NGINX là HTTP server, Postfix là SMTP server thì đây là nameserver (DNS server).

Tuesday, 14 February 2017

[systemd] journalctl cho người mới tập chơi

Lên Ubuntu 16.04, hệ thống init Upstart đã được thay thế bằng hệ thống systemd, kèm theo nhiều thay đổi khác, trong đó đáng chú ý nhất là cách xem log trên server mãi mãi thay đổi từ đây.
Không còn syslog, rsyslog ... nữa mà tất cả log đổ vào một chương trình duy nhất, có tên journald

Tìm hiểu câu lệnh journalctl

sử dụng các câu lệnh tìm hiểu câu lệnh
$ for cmd in whatis whereis; do $cmd journalctl; done
journalctl (1)       - Query the systemd journal
journalctl: /bin/journalctl /usr/share/man/man1/journalctl.1.gz
$ dpkg -S $(which journalctl)
systemd: /bin/journalctl
Vậy journalctl được cung cấp bởi package ``systemd``, nó là một cộng cụ để query (truy vấn) "the systemd journal" (journald).

systemd journal

là một daemon , một bộ phận của systemd, là một system service thực hiện thu thập và lưu trữ dữ liệu logging. Nó nhận dữ liệu từ nhiều nguồn như kernel log message (kmsg), system log messages (syslog), stdout/stderr của các system service, audit record thông qua hệ thống audit.

Mặc định log sẽ được chứa trong /run/log/journal/, bởi dữ liệu trong /run sẽ bị mất sau khi reboot, log cũng sẽ bị mất theo (có thể config để thay đổi điều này).

Các câu lệnh journalctl thường dùng

Monday, 13 February 2017

[Python] function là object

Nếu có coi function là một cái gì đó đặc biệt, khác biệt với mọi thứ còn lại trong Python thì có thể bạn đã sai, hay ít nhất cũng cần nghĩ lại sau khi xem các đặc điểm của function trong bài này.

Python lambda
Function là object, như mọi thứ khác trong Python. Integer là object, string là object, và function không phải ngoại lệ.
$ python3
>>> type(5)
<class 'int'>
>>> type('http://pymi.vn')
<class 'str'>
>>> type(True)
<class 'bool'>
>>> type(len)
<class 'builtin_function_or_method'>
Nếu biết về lambda trong Python, bạn sẽ thấy rõ hơn function là cái gì:
>>> double = lambda x: x*2
>>> type(double)
<class 'function'>
>>> double(21)
42
lambda tạo ra 1 object kiểu function (tưởng tượng ra 1 cục tròn tròn), sau đó ta gán (bind) tên ``double`` cho object đó. Nó tương tự như việc gán tên cho 1 string:

>>> name = "PyMI"
>>> type(name)
<class 'str'>
name -----> (string PyMI)
double ------> (function x:x*2)

Bản chất cú pháp định nghĩa function trong python là nó sẽ tạo ra một function object, và bind cái tên của function vào object ấy.
>>> def double(x):
...     return x*2
...
>>> type(double)
<class 'function'>
>>> double(7)
14
Để bind một cái tên khác vào cục function object này, gán tên mới như bình thường:
>>> gap_doi = double
>>> gap_doi(7)
14
Phép gán (=) thực chất nói rằng: tạo ra một cái tên và bind nó vào cái cục object mà double đang bind tới.

gap_doi ----> (function object) <----- double

Function là  callable object

function là object như bao object khác, điều mà khiến nó có vẻ khác biệt là bởi khả năng "gọi" được, tức viết được theo dạng
function(argument) 
khả năng này có được nhờ function object có method tên là __call__.
Một object có method __call__ gọi là 1 callable object: có thể kể tới function, class hay bất cứ kiểu dữ liệu nào thoả mãn điều kiện ấy.
>>> len.__call__([1,2,3])
3
>>> len([1,2,3])
3
>>> class Foo():
...     pass
...
>>> Foo.__call__()
<__main__.Foo object at 0x10d8c3b70>
>>> Foo()
<__main__.Foo object at 0x10d8c3898>

Function nhận đầu vào là function 

Function mà nhận function khác làm đầu vào được gọi là "higher order function".

``len`` là một function
>>> id(len)
4517823904
id là function có thể nhận đầu vào là một function, id là một "higher order function".

Function ``do`` sau đây là một higher order function:
>>> def do(function, argument):
...     return function(argument)
...
>>> result = do(lambda x:x*2, 'Python')
>>> print(result)
PythonPython
Các decorator đều là các higher order function.

map

map là higher order function thường được dùng.
map nhận đầu vào là 1 function và 1 list (chính xác hơn thì là iterable), rồi map gọi function ấy với từng phần tử của list, thu được 1 tập các kết quả sau khi gọi function.
>>> map(lambda x: x*2, [1,3,5])
<map object at 0x10d6b5128>
>>> list(map(lambda x: x*2, [1,3,5]))
[2, 6, 10]
Nếu không thấy quen dùng lambda, có thể viết dài hơn sử dụng cú pháp def:
>>> def double(n):
...     return n*2
...
>>> list(map(double, [1,3,5]))
[2, 6, 10]
Function là object, tức ta có thể tạo 1 list các function:
>>> funcs = [id, type, str, len]
>>> type(funcs[0])
<class 'builtin_function_or_method'>
>>> for func in funcs:
...     print(func.__name__, func(['Python', 'Golang']))
...
id 4520086984
type <class 'list'>
str ['Python', 'Golang']
len 2

Lambda với list comprehension

Tạo 5 function, mỗi function in một số từ 0 đến 4
Tạo 1 list 5 function bằng list comprehension:
>>> fs = [lambda: print(x) for x in range(5)]
>>> for f in fs:
...     f()
...
4
4
4
4
4
Cả 5 function ở đây đều refer đến "i", vì thế ta không thu được 5 functions lần lượt in từ 0 đến 4.
Cách làm đúng:
>>> import functools
>>> for f in [functools.partial(lambda x:print(x), x) for x in range(5)]:
...     f()
...
0
1
2
3
4
Function là object.
Hết.
Welcome to functional programming and Python 😱
HVN at http://www.familug.org/ and http://pymi.vn 

Wednesday, 8 February 2017

Cấu hình hệ thống email để không bị quẳng vào Spam


Mail spam và OpenDKIM

Cài đặt và vận hành 1 hệ thống email là một trong những thứ lằng nhằng nhất khi làm sysadmin.
Một hệ thống email hoàn chỉnh hoạt động sẽ cần đến khoảng một chục thành phần khác nhau, nhưng hoạt động là chưa đủ, để email gửi ngon lành, không bị vào Spam thì phải hiểu thêm một đống thứ nữa.

Khi gửi mail đến Gmail, hệ thống Gmail sẽ coi mail của bạn là spam nếu email đó chưa authenticated. Xem đầy đủ tại đây: https://support.google.com/mail/answer/81126?hl=en#authentication

Google yêu cầu hệ thống của bạn khi gửi mail phải
- "Sign message" bằng DKIM
- Phải có SPF DNS record tương ứng với hệ thống mail
- Phải có DMARC policy (DNS record)

Một hệ thống mail chỉ có mỗi mail server (MTA-E.g: postfix) thì khả năng vào sọt rác là rất cao, thậm chí còn bị Gmail reject , không nhận mail.

Cài đặt OpenDKIM

Xem tại Ubuntu wiki ArchLinux wiki
chú ý: khi dùng với postfix, mặc dù đã config
cho postfix đọc từ UNIX socket trong /var/run/opendkim nhưng sẽ báo lỗi

postfix/smtpd: warning: connect to Milter service unix:/var/run/opendkim/opendkim.sock: No such file or directory
Mặc dù file đó có tồn tại.
Bởi nếu postfix được cấu hình chroot thì file thực sự mà postfix cần đọc là
/var/spool/postfix/var/run/opendkim/
Nếu đã đổi path mà gặp lỗi mới: Permission Denied thì cần chỉnh lại cho user opendkim và postfix đều có thể đọc ghi được socket này. Xem thêm tại stackexchange.

Kiểm tra mail có bị coi là spam không?

Để kiểm tra 1 email có khả năng gửi thành công không, gửi mail tới điạ chỉ cung cấp bởi https://www.mail-tester.com/ và xem kết quả. Nếu được 10/10 điểm thì mới có khả năng vào inbox.



Sau khi mail đã config đầy đủ, gửi mail đi và Gmail nhận được, ta có thể mở mail ra và chọn "Show original" để xem chi tết các header.

Sunday, 5 February 2017

Dựng HTTP server bằng một câu lệnh

Đôi khi ta cần dựng 1 HTTP server, trong một nháy mắt 😎
Để:
- xem nội dung 1 file html
- chia sẻ file siêu tốc (tất nhiên không an toàn, ai có link cũng tải được)

Giải pháp:
- mọi giải pháp cần file config như NGINX, Apache đều bị loại do vượt quá yêu cầu thiết lập dưới 1 phút.

Các giải pháp được đề cử 😍

busybox

Nếu không có sẵn trên máy
# apt-get install -y busybox
$ busybox httpd -f -p 9999 -h
-f : chạy foreground
-p: port
-h: home, thư mục sẽ phục vụ, mặc định là thư mục hiện tại
Khi truy cập vào IP:9999, ta sẽ nhận được response 404 nếu không có file index.html nào trong thư mục được phục vụ. Để truy cập các file khác, ta phải tự chọn trên URL chứ không có danh sách.
Ví dụ: để tải file destroy.mp4 trong thư mục phục vụ , truy cập vào http://IP:9999/destroy.mp4

Twisted 

là một Python package
trước tiên phải cài twisted:
$ pip install twisted
$ twistd -n web -p 8080 --path .
Truy cập IP:8080 sẽ nhận được danh sách các file trong thư mục chỉ ra bởi --path

Python2

$ python -m SimpleHTTPServer 8000
Khi truy cập vào IP:8000 ta sẽ thấy danh sách các file của thư mục ta chạy câu lệnh.

Python 3