pgsqlpot 2.0.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. core/__init__.py +0 -0
  2. core/config.py +50 -0
  3. core/logfile.py +74 -0
  4. core/output.py +39 -0
  5. core/paths.py +53 -0
  6. core/protocol.py +161 -0
  7. core/tools.py +170 -0
  8. output_plugins/__init__.py +0 -0
  9. output_plugins/couch.py +68 -0
  10. output_plugins/datadog.py +74 -0
  11. output_plugins/discord.py +133 -0
  12. output_plugins/elastic.py +137 -0
  13. output_plugins/hpfeed.py +43 -0
  14. output_plugins/influx2.py +66 -0
  15. output_plugins/jsonlog.py +36 -0
  16. output_plugins/kafka.py +57 -0
  17. output_plugins/localsyslog.py +66 -0
  18. output_plugins/mongodb.py +83 -0
  19. output_plugins/mysql.py +210 -0
  20. output_plugins/nlcvapi.py +119 -0
  21. output_plugins/postgres.py +154 -0
  22. output_plugins/redisdb.py +47 -0
  23. output_plugins/rethinkdblog.py +46 -0
  24. output_plugins/slack.py +94 -0
  25. output_plugins/socketlog.py +40 -0
  26. output_plugins/sqlite.py +141 -0
  27. output_plugins/telegram.py +141 -0
  28. output_plugins/textlog.py +46 -0
  29. output_plugins/xmpp.py +193 -0
  30. pgsqlpot/__init__.py +25 -0
  31. pgsqlpot/cli.py +512 -0
  32. pgsqlpot/data/Dockerfile +56 -0
  33. pgsqlpot/data/docs/INSTALL.md +400 -0
  34. pgsqlpot/data/docs/INSTALLWIN.md +411 -0
  35. pgsqlpot/data/docs/PLUGINS.md +21 -0
  36. pgsqlpot/data/docs/TODO.md +8 -0
  37. pgsqlpot/data/docs/datadog/README.md +32 -0
  38. pgsqlpot/data/docs/discord/README.md +58 -0
  39. pgsqlpot/data/docs/geoipupdtask.ps1 +270 -0
  40. pgsqlpot/data/docs/mysql/README.md +176 -0
  41. pgsqlpot/data/docs/mysql/READMEWIN.md +157 -0
  42. pgsqlpot/data/docs/mysql/mysql.sql +85 -0
  43. pgsqlpot/data/docs/postgres/README.md +184 -0
  44. pgsqlpot/data/docs/postgres/READMEWIN.md +196 -0
  45. pgsqlpot/data/docs/postgres/postgres.sql +73 -0
  46. pgsqlpot/data/docs/slack/README.md +68 -0
  47. pgsqlpot/data/docs/sqlite3/README.md +131 -0
  48. pgsqlpot/data/docs/sqlite3/READMEWIN.md +123 -0
  49. pgsqlpot/data/docs/sqlite3/sqlite3.sql +69 -0
  50. pgsqlpot/data/docs/telegram/README.md +103 -0
  51. pgsqlpot/data/etc/honeypot.cfg +415 -0
  52. pgsqlpot/data/etc/honeypot.cfg.base +418 -0
  53. pgsqlpot/data/test/.gitignore +3 -0
  54. pgsqlpot/data/test/test.py +51 -0
  55. pgsqlpot/honeypot.py +117 -0
  56. pgsqlpot-2.0.0.dist-info/METADATA +152 -0
  57. pgsqlpot-2.0.0.dist-info/RECORD +61 -0
  58. pgsqlpot-2.0.0.dist-info/WHEEL +6 -0
  59. pgsqlpot-2.0.0.dist-info/entry_points.txt +2 -0
  60. pgsqlpot-2.0.0.dist-info/licenses/LICENSE +674 -0
  61. pgsqlpot-2.0.0.dist-info/top_level.txt +3 -0
pgsqlpot/cli.py ADDED
@@ -0,0 +1,512 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ """
6
+ pgsqlpot command-line entry point.
7
+
8
+ After `pip install pgsqlpot` the user gets a `pgsqlpot` command:
9
+
10
+ pgsqlpot init [DIR] -- scaffold a working directory (default: cwd)
11
+ pgsqlpot run -- start the honeypot in the foreground
12
+ pgsqlpot start -- start in the background (Linux, macOS, Windows)
13
+ pgsqlpot stop -- stop a backgrounded honeypot
14
+ pgsqlpot restart -- restart (stop and start in the backgrounded) the honeypot
15
+ pgsqlpot status -- show running status
16
+
17
+ The working directory is resolved in this order:
18
+ 1. DIR argument (for init) or --workdir flag (for other commands)
19
+ 2. PGSLQPOT_WORKDIR environment variable
20
+ 3. Current working directory
21
+ """
22
+
23
+ from __future__ import absolute_import, print_function
24
+
25
+ from argparse import ArgumentParser
26
+ from importlib import import_module
27
+ from os import (
28
+ chdir,
29
+ devnull,
30
+ dup2,
31
+ environ,
32
+ kill,
33
+ makedirs,
34
+ name,
35
+ remove,
36
+ walk,
37
+ O_RDWR,
38
+ )
39
+ from os import open as _open
40
+ from os.path import (
41
+ abspath,
42
+ dirname,
43
+ exists,
44
+ isdir,
45
+ join,
46
+ relpath,
47
+ )
48
+ from re import search, IGNORECASE
49
+ from shutil import copy2
50
+ import sys
51
+
52
+ from core.paths import bundled, get_workdir, workdir_path
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # helpers
57
+ # ---------------------------------------------------------------------------
58
+
59
+ def _ensure_dir(p):
60
+ if not isdir(p):
61
+ makedirs(p)
62
+
63
+
64
+ def _file_differs(a, b):
65
+ with open(a, 'r') as fa, open(b, 'r') as fb:
66
+ return fa.read() != fb.read()
67
+
68
+
69
+ def _copy_if_missing(src, dst, label=None):
70
+ # Guard against copying a file onto itself (e.g. in a source checkout).
71
+ if abspath(src) == abspath(dst):
72
+ return False
73
+ if not exists(dst) or _file_differs(src, dst):
74
+ copy2(src, dst)
75
+ if label is not None:
76
+ print(' copied {}'.format(label))
77
+ return True
78
+ if label is not None:
79
+ print(' exists {}'.format(label))
80
+ return False
81
+
82
+
83
+ def _set_workdir(path):
84
+ environ['PGSLQPOT_WORKDIR'] = abspath(path)
85
+
86
+
87
+ def _pidfile():
88
+ return workdir_path('pgsqlpot.pid')
89
+
90
+
91
+ def _pid_running(pid):
92
+ if name == 'nt':
93
+ # os.kill(pid, 0) is a Unix idiom that doesn't work on Windows -
94
+ # it raises SystemError. Use OpenProcess instead: a non-NULL handle
95
+ # means the process exists; NULL means it doesn't (or we lack
96
+ # permission, but that also means it's not ours to manage).
97
+ import ctypes
98
+ SYNCHRONIZE = 0x00100000
99
+ handle = ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, pid)
100
+ if not handle:
101
+ return False
102
+ ctypes.windll.kernel32.CloseHandle(handle)
103
+ return True
104
+ try:
105
+ kill(pid, 0)
106
+ return True
107
+ except OSError:
108
+ return False
109
+
110
+
111
+ def _locate_honeypot_script():
112
+ """Return the absolute path to honeypot.py inside the installed package."""
113
+ # honeypot.py is bundled inside the pgsqlpot package directory alongside
114
+ # this file (cli.py). Using __file__ avoids any dependency on pkg_resources
115
+ # or importlib.resources and works on Python 2.7+.
116
+ here = dirname(abspath(__file__))
117
+ script_path = join(here, 'honeypot.py')
118
+ if not exists(script_path):
119
+ raise RuntimeError(
120
+ 'Cannot locate honeypot.py. '
121
+ 'Expected inside the pgsqlpot package directory: {}'.format(here)
122
+ )
123
+ return script_path
124
+
125
+
126
+ def _run_main():
127
+ """
128
+ Import and run honeypot.main().
129
+
130
+ honeypot.py lives inside the pgsqlpot package directory. We import it
131
+ directly using importlib so it runs as a proper module rather than a
132
+ top-level script, avoiding any sys.path manipulation.
133
+ """
134
+ script_path = _locate_honeypot_script()
135
+ script_dir = dirname(script_path)
136
+
137
+ # Ensure the package directory is on sys.path so that honeypot.py's own
138
+ # imports (from core.xxx, from output_plugins.xxx, etc.) resolve correctly.
139
+ if script_dir not in sys.path:
140
+ sys.path.insert(0, script_dir)
141
+
142
+ # Import as 'honeypot'; clear any stale cached version first.
143
+ if 'honeypot' in sys.modules:
144
+ del sys.modules['honeypot']
145
+ mod = import_module('honeypot')
146
+ mod.main()
147
+
148
+
149
+ # ---------------------------------------------------------------------------
150
+ # init
151
+ # ---------------------------------------------------------------------------
152
+
153
+ def cmd_init(args):
154
+
155
+ target = abspath(args.dir)
156
+ _set_workdir(target)
157
+ print('Initialising pgsqlpot working directory: {}'.format(target))
158
+
159
+ # Runtime directories
160
+ for d in ('etc', 'log', 'data'):
161
+ _ensure_dir(join(target, d))
162
+ print(' mkdir {}'.format(d))
163
+
164
+ # Config templates
165
+ for fname in ('honeypot.cfg.base',):
166
+ src = bundled('etc', fname)
167
+ dst = join(target, 'etc', fname)
168
+ _copy_if_missing(src, dst, join('etc', fname))
169
+
170
+ # docs/ tree - walk the entire bundled docs directory and copy everything.
171
+ docs_src = bundled('docs')
172
+ docs_dst = join(target, 'docs')
173
+ for dirpath, dirnames, filenames in walk(docs_src):
174
+ rel_dir = relpath(dirpath, docs_src)
175
+ dst_dir = join(docs_dst, rel_dir) if rel_dir != '.' else docs_dst
176
+ _ensure_dir(dst_dir)
177
+ for fname in filenames:
178
+ # geoipupdtask.ps1 is handled separately below (Windows only)
179
+ if fname == 'geoipupdtask.ps1':
180
+ continue
181
+ src = join(dirpath, fname)
182
+ dst = join(dst_dir, fname)
183
+ rel = join('docs', rel_dir, fname) if rel_dir != '.' else join('docs', fname)
184
+ _copy_if_missing(src, dst, rel)
185
+
186
+ # These files are copied silently to the working directory root.
187
+ _copy_if_missing(bundled('test', 'test.py'), join(target, 'test.py'))
188
+ _dockerfile = bundled('Dockerfile')
189
+ if exists(_dockerfile):
190
+ _copy_if_missing(_dockerfile, join(target, 'Dockerfile'))
191
+ # geoipupdtask.ps1 is a Windows Task Scheduler helper - copy on Windows only
192
+ if name == 'nt':
193
+ geo_src = bundled('docs', 'geoipupdtask.ps1')
194
+ if exists(geo_src):
195
+ _copy_if_missing(geo_src, join(target, 'geoipupdtask.ps1'))
196
+
197
+ print()
198
+ print('Next steps:')
199
+ print(' 1. Edit {} with your settings.'.format(join(target, 'etc', 'honeypot.cfg')))
200
+ print(' 2. Place GeoLite2-City.mmdb and GeoLite2-ASN.mmdb in {}.'.format(join(target, 'data')))
201
+ print(' 3. Run: pgsqlpot run (foreground)')
202
+ print(' or: pgsqlpot start (background)')
203
+
204
+
205
+ # ---------------------------------------------------------------------------
206
+ # run (foreground)
207
+ # ---------------------------------------------------------------------------
208
+
209
+ def cmd_run(args):
210
+ # honeypot.py has its own argparse parser and will see sys.argv directly.
211
+ # Keep only arguments that come after 'run' so honeypot.py's parser sees
212
+ # only the options intended for it (e.g. pgsqlpot run --port 5432).
213
+ try:
214
+ run_idx = sys.argv.index('run')
215
+ sys.argv = [sys.argv[0]] + sys.argv[run_idx + 1:]
216
+ except ValueError:
217
+ sys.argv = [sys.argv[0]]
218
+
219
+ # Output plugins use relative paths from CONFIG (e.g. 'log/pgsqlpot.json')
220
+ # without going through workdir_path(). Changing to the working directory
221
+ # before starting ensures these paths resolve correctly.
222
+ chdir(get_workdir())
223
+
224
+ _run_main()
225
+
226
+
227
+ # ---------------------------------------------------------------------------
228
+ # start (background) -- cross-platform
229
+ # ---------------------------------------------------------------------------
230
+
231
+ def cmd_start(args):
232
+ pidfile = _pidfile()
233
+
234
+ # Check if already running
235
+ if exists(pidfile):
236
+ with open(pidfile) as fh:
237
+ pid_str = fh.read().strip()
238
+ if pid_str:
239
+ try:
240
+ pid = int(pid_str)
241
+ if _pid_running(pid):
242
+ print('The honeypot is already running (PID {}).'.format(pid))
243
+ return
244
+ except ValueError:
245
+ pass
246
+
247
+ print('Starting the honeypot...')
248
+ if name == 'nt':
249
+ _start_windows(pidfile)
250
+ else:
251
+ _start_posix(pidfile)
252
+
253
+
254
+ def _start_posix(pidfile):
255
+ """Fork to background on POSIX (Linux, macOS)."""
256
+ from os import fork, setsid
257
+ pid = fork()
258
+ if pid > 0:
259
+ # Parent: record child PID and exit
260
+ with open(pidfile, 'w') as fh:
261
+ fh.write(str(pid))
262
+ print('The honeypot was started successfully (PID {}).'.format(pid))
263
+ return
264
+
265
+ # Child: detach from terminal
266
+ setsid()
267
+ _devnull = _open(devnull, O_RDWR)
268
+ for fd in (0, 1, 2):
269
+ dup2(_devnull, fd)
270
+
271
+ # Clear the 'start' subcommand from sys.argv before honeypot.py's parser runs
272
+ try:
273
+ start_idx = sys.argv.index('start')
274
+ sys.argv = [sys.argv[0]] + sys.argv[start_idx + 1:]
275
+ except ValueError:
276
+ sys.argv = [sys.argv[0]]
277
+
278
+ # Ensure relative paths in plugin configs resolve against the working directory
279
+ chdir(get_workdir())
280
+
281
+ _run_main()
282
+
283
+
284
+ def _start_windows(pidfile):
285
+ """
286
+ Spawn a detached, hidden background process on Windows.
287
+
288
+ Uses pythonw.exe (the windowless interpreter) to avoid any console window.
289
+ DETACHED_PROCESS + CREATE_NO_WINDOW ensure the child survives parent exit
290
+ and has no console of its own.
291
+ """
292
+ from subprocess import Popen, STARTF_USESHOWWINDOW, STARTUPINFO
293
+
294
+ # Defined as literals for Python 2.7/3.6 compatibility - these constants
295
+ # were only added to subprocess as named attributes in Python 3.7.
296
+ DETACHED_PROCESS = 0x00000008
297
+ CREATE_NO_WINDOW = 0x08000000
298
+
299
+ startupinfo = STARTUPINFO()
300
+ startupinfo.dwFlags |= STARTF_USESHOWWINDOW
301
+ startupinfo.wShowWindow = 0 # SW_HIDE
302
+
303
+ # pythonw.exe is the windowless interpreter Windows ships for background
304
+ # scripts; more reliable than CREATE_NO_WINDOW for Twisted-based processes.
305
+ pythonw = join(dirname(sys.executable), 'pythonw.exe')
306
+ interpreter = pythonw if exists(pythonw) else sys.executable
307
+ cmd = [interpreter, '-m', 'pgsqlpot.cli', 'run']
308
+
309
+ _devnull = open(devnull, 'wb')
310
+ try:
311
+ proc = Popen(
312
+ cmd,
313
+ creationflags=DETACHED_PROCESS | CREATE_NO_WINDOW,
314
+ startupinfo=startupinfo,
315
+ stdin=_devnull,
316
+ stdout=_devnull,
317
+ stderr=_devnull,
318
+ env=environ.copy(),
319
+ )
320
+ finally:
321
+ _devnull.close()
322
+
323
+ with open(pidfile, 'w') as fh:
324
+ fh.write(str(proc.pid))
325
+ print('The honeypot was started successfully (PID {}).'.format(proc.pid))
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # stop
330
+ # ---------------------------------------------------------------------------
331
+
332
+ def cmd_stop(args):
333
+ pidfile = _pidfile()
334
+ if not exists(pidfile):
335
+ print('The honeypot is not running (no PID file).')
336
+ return
337
+
338
+ with open(pidfile) as fh:
339
+ pid_str = fh.read().strip()
340
+
341
+ if not pid_str:
342
+ remove(pidfile)
343
+ print('Empty PID file removed.')
344
+ return
345
+
346
+ try:
347
+ pid = int(pid_str)
348
+ except ValueError:
349
+ remove(pidfile)
350
+ print('Corrupt PID file removed.')
351
+ return
352
+
353
+ if not _pid_running(pid):
354
+ remove(pidfile)
355
+ print('Stale PID file (PID {} not running) removed.'.format(pid))
356
+ return
357
+
358
+ print('Stopping the honeypot (PID {})... '.format(pid), end='')
359
+
360
+ if name == 'nt':
361
+ _stop_windows(pid, pidfile)
362
+ else:
363
+ _stop_posix(pid, pidfile)
364
+
365
+
366
+ def _stop_posix(pid, pidfile):
367
+ from signal import SIGKILL, SIGTERM
368
+ from time import sleep
369
+ kill(pid, SIGTERM)
370
+ for _ in range(60):
371
+ sleep(1)
372
+ if not _pid_running(pid):
373
+ print('Stopped.')
374
+ remove(pidfile)
375
+ return
376
+ print()
377
+ print('Did not stop gracefully; sending SIGKILL.')
378
+ kill(pid, SIGKILL)
379
+ remove(pidfile)
380
+
381
+
382
+ def _stop_windows(pid, pidfile):
383
+ """
384
+ Terminate the background honeypot process on Windows.
385
+
386
+ The process was launched with pythonw.exe + CREATE_NO_WINDOW so it has no
387
+ console. GenerateConsoleCtrlEvent only works for processes sharing a
388
+ console or in the same process group - neither applies here. We therefore
389
+ go straight to TerminateProcess via taskkill /F.
390
+ """
391
+ from subprocess import call
392
+
393
+ _devnull = open(devnull, 'wb')
394
+ try:
395
+ ret = call(
396
+ ['taskkill', '/F', '/PID', str(pid)],
397
+ stdout=_devnull, stderr=_devnull,
398
+ )
399
+ finally:
400
+ _devnull.close()
401
+
402
+ if ret == 0 or not _pid_running(pid):
403
+ print('Stopped.')
404
+ else:
405
+ print()
406
+ print('Warning: taskkill returned {}, process may still be running.'.format(ret))
407
+ remove(pidfile)
408
+
409
+
410
+ # ---------------------------------------------------------------------------
411
+ # restart
412
+ # ---------------------------------------------------------------------------
413
+
414
+ def cmd_restart(args):
415
+ cmd_stop(args)
416
+ cmd_start(args)
417
+
418
+
419
+ # ---------------------------------------------------------------------------
420
+ # status
421
+ # ---------------------------------------------------------------------------
422
+
423
+ def cmd_status(args):
424
+ pidfile = _pidfile()
425
+ if not exists(pidfile):
426
+ print('The honeypot is not running.')
427
+ return
428
+ with open(pidfile) as fh:
429
+ pid_str = fh.read().strip()
430
+ if not pid_str:
431
+ remove(pidfile)
432
+ print('The honeypot is not running (empty PID file removed).')
433
+ return
434
+ try:
435
+ pid = int(pid_str)
436
+ except ValueError:
437
+ remove(pidfile)
438
+ print('Corrupt PID file removed.')
439
+ return
440
+ if _pid_running(pid):
441
+ print('The honeypot is running (PID: {}).'.format(pid))
442
+ else:
443
+ remove(pidfile)
444
+ print('The honeypot is not running (stale PID file for PID {} removed).'.format(pid))
445
+
446
+
447
+ # ---------------------------------------------------------------------------
448
+ # entry point
449
+ # ---------------------------------------------------------------------------
450
+
451
+ def main():
452
+ parser = ArgumentParser(
453
+ prog='pgsqlpot',
454
+ description='pgsqlpot - A PostgreSQL Honeypot',
455
+ )
456
+
457
+ # Read version from honeypot.py without importing it (avoids Twisted import
458
+ # at startup) and without using pkg_resources (deprecated in setuptools 81+).
459
+ try:
460
+ with open(_locate_honeypot_script()) as fh:
461
+ _content = fh.read()
462
+ _m = search(r"__VERSION__\s*=\s*['\"]([^'\"]+)['\"]", _content, IGNORECASE)
463
+ _version = _m.group(1) if _m else 'unknown'
464
+ except Exception:
465
+ _version = 'unknown'
466
+
467
+ parser.add_argument('-v', '--version', action='version', version=_version)
468
+ parser.add_argument(
469
+ '-w', '--workdir', default=None,
470
+ help='Working directory (overrides PGSLQPOT_WORKDIR and cwd)'
471
+ )
472
+
473
+ sub = parser.add_subparsers(dest='command', metavar='COMMAND')
474
+ sub.required = True
475
+
476
+ p_init = sub.add_parser('init', help='Scaffold a working directory')
477
+ p_init.add_argument(
478
+ 'dir', nargs='?', default='.',
479
+ help='Directory to initialize (default: current directory)'
480
+ )
481
+ p_init.set_defaults(func=cmd_init)
482
+
483
+ p_run = sub.add_parser('run', help='Start the honeypot in the foreground')
484
+ p_run.set_defaults(func=cmd_run)
485
+
486
+ p_start = sub.add_parser('start', help='Start the honeypot in the background')
487
+ p_start.set_defaults(func=cmd_start)
488
+
489
+ sub.add_parser('stop', help='Stop the backgrounded honeypot').set_defaults(func=cmd_stop)
490
+
491
+ p_restart = sub.add_parser(
492
+ 'restart', help='Restart (stop and start) the honeypot in the background'
493
+ )
494
+ p_restart.set_defaults(func=cmd_restart)
495
+
496
+ for _p in (p_run, p_start, p_restart):
497
+ _p.add_argument('-p', '--port', type=int, help='Port to listen on (default: 5432)')
498
+ _p.add_argument('-l', '--logfile', help='Log file (default: stdout)')
499
+ _p.add_argument('-s', '--sensor', help='Sensor name (default: hostname)')
500
+
501
+ sub.add_parser('status', help='Show running status').set_defaults(func=cmd_status)
502
+
503
+ args = parser.parse_args()
504
+
505
+ if args.workdir:
506
+ _set_workdir(args.workdir)
507
+
508
+ args.func(args)
509
+
510
+
511
+ if __name__ == '__main__':
512
+ main()
@@ -0,0 +1,56 @@
1
+ # syntax=docker/dockerfile:1
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Build stage: install pgsqlpot and its dependencies.
5
+ # The -dev variant has pip and build tools; the runtime variant does not.
6
+ # ---------------------------------------------------------------------------
7
+ FROM dhi.io/python:3-dev AS builder
8
+
9
+ ARG VERSION
10
+ ARG PLUGINS=all
11
+
12
+ # Install pgsqlpot (and selected plugin dependencies) into a prefix that
13
+ # we can copy wholesale into the runtime stage.
14
+ RUN pip install --prefix=/install --prefer-binary \
15
+ "pgsqlpot==${VERSION}${PLUGINS:+[${PLUGINS}]}"
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Runtime stage: minimal hardened image, no shell, no package manager.
19
+ #
20
+ # SECURITY NOTE: Running this container requires the host user to be a member
21
+ # of the 'docker' group, which grants effective root access on the host.
22
+ # This negates the benefit of running the honeypot as a restricted user.
23
+ # Consider using Podman instead, which does not require privileged access:
24
+ # https://podman.io/
25
+ # ---------------------------------------------------------------------------
26
+ FROM dhi.io/python:3
27
+
28
+ ARG VERSION
29
+ LABEL maintainer="Bontchev"
30
+ LABEL name="pgsqlpot"
31
+ LABEL version="${VERSION}"
32
+
33
+ # Copy the installed packages from the build stage
34
+ COPY --from=builder /install /usr/local
35
+
36
+ # Run as a non-root user inside the container.
37
+ # Note: this does not mitigate the host-level docker group privilege issue
38
+ # described above, but it limits post-exploitation options inside the container.
39
+ RUN useradd --no-create-home --shell /bin/false pgsqlpot
40
+
41
+ EXPOSE 5432
42
+
43
+ # Scaffold the working directory and hand ownership to the non-root user.
44
+ WORKDIR /pgsqlpot
45
+ RUN pgsqlpot init && chown -R pgsqlpot:pgsqlpot /pgsqlpot
46
+
47
+ USER pgsqlpot
48
+
49
+ # Mount your honeypot.cfg and GeoLite2 databases at runtime, e.g.:
50
+ #
51
+ # docker run \
52
+ # -v /path/to/honeypot.cfg:/pgsqlpot/etc/honeypot.cfg \
53
+ # -v /path/to/data:/pgsqlpot/data \
54
+ # -p 5432:5432 pgsqlpot
55
+ #
56
+ CMD ["pgsqlpot", "run"]