Compare commits

..

200 Commits

Author SHA1 Message Date
Luke J Powlett
9f5982d9a8 MQ container 9.2.0.0-r2 2020-09-28 10:49:36 +01:00
Luke Powlett
0627588121 Updated to latest UBI version 2020-09-28 10:49:36 +01:00
Luke Powlett
a3ef263487 Updated to go 1.13.15, switched to community goloang image 2020-09-28 10:49:36 +01:00
KIRAN DARBHA
fc0da7352b addressing review comments 2020-09-28 10:49:36 +01:00
KIRAN DARBHA
9dc80316ad fat-manifest updates 2020-09-28 10:49:36 +01:00
KIRAN DARBHA
6dd60349fe fat-manifest updates
fat-manifest updates

fat-manifest updates

Making create-manifest-list.sh executable

fixing travis failure

fixing fat-manifests

fat-manifest

fat-manifest

fat-manifest updates

fat-manifests

fat-manifest updates

manifest-list updates

fat-manifest

updating fat-manifests

fat-manifests
2020-09-28 10:49:36 +01:00
KIRAN DARBHA
fda918db20 addressing review comments 2020-09-28 10:49:36 +01:00
KIRAN DARBHA
7c89606782 adding zlinux-support 2020-09-28 10:49:36 +01:00
KIRAN DARBHA
666b23e7f0 adding zlinux-support 2020-09-28 10:49:36 +01:00
Thomas-Apter
7a1d39e303 Changed testming.md to use make advancedserver 2020-09-28 10:49:36 +01:00
Thomas-Apter
ba40b6d220 Create advancedserver make target, and update docs to use 'make advancedserver' 2020-09-28 10:49:36 +01:00
Thomas-Apter
39508186c8 Changed 'ran' to 'run' 2020-09-28 10:49:36 +01:00
Thomas-Apter
372f2e4a36 Reformatted testing.md and adding required step make build-devjmstest to Docker tests instructions 2020-09-28 10:49:36 +01:00
Luke Powlett
cd69f6287f MQ 9.2 doc updates 2020-07-28 12:34:19 +01:00
Luke Powlett
7dee4c82aa Updated go-toolset version 2020-07-16 09:55:20 +01:00
Luke Powlett
dc4675b99a Fix for MQ 9.2 installation name env var 2020-07-16 09:55:20 +01:00
Luke Powlett
ba493cbeb3 Updated licenses for 9.2, UBI version 2020-07-16 09:55:20 +01:00
VENKATA KIRAN KUMAR DARBHA
872050a2cd Merge pull request #96 from mq-cloudpak/9.2MQAdvDevImage
adding mqadv developer edition driver download location
2020-07-08 18:29:38 +05:30
KIRAN DARBHA
4737a8b660 adding mqadv developer edition driver download location 2020-07-08 11:16:42 +05:30
Stephen D Marshall
9b81aedd9a Mqsc (#95)
* Change to using MQSC option on crtmqm

* Fix docker tests

* Remove function configureQueueManager
2020-07-06 14:43:14 +01:00
Luke Powlett
c64c6fe95d Updated change log 2020-06-30 09:50:55 +01:00
Luke Powlett
a53fb7f49a Removed custom OAM patch 2020-06-30 09:50:55 +01:00
Luke Powlett
d95e44f57c Added 9.2 dockerhub fat manifest 2020-06-30 09:50:55 +01:00
Luke Powlett
4b19af1dfe Updated to MQ v9.2.0 2020-06-30 09:50:55 +01:00
Arthur Barr
b4949aaf4f Append inserts to mirrored error log messages 2020-06-29 09:51:35 +01:00
Luke Powlett
b9d48aa980 Updated release process for operators 2020-05-27 11:05:28 +01:00
Amrit Kandola
59baa97e91 Updated UBI Image and Go ToolSet 2020-05-26 12:19:55 +01:00
Lewis Weedon
394cb56ba0 Peer review changes 2020-05-12 15:48:46 +01:00
Lewis Weedon
62a2d6ef96 Dockerfile in shellscript changes 2020-05-12 15:48:46 +01:00
Nicholas J Daffern
dcfebc38bd Merge pull request #85 from mq-cloudpak/webserverFix
Change to allow webserver to start
2020-04-30 17:56:14 +01:00
Nicholas-Daffern
1ffc598064 Change to allow webserver to start 2020-04-30 16:15:43 +01:00
Lewis Weedon
fee0eac14c Change to dockerfile-server to add maintainer label 2020-04-28 10:53:24 +01:00
Luke Powlett
c56e305aec Override libedit load to suppress failing load in UBI8 2020-03-30 18:13:52 +01:00
Luke Powlett
1bb39bc9fd Re-enable multi-arch dev build/release 2020-03-30 10:45:01 +01:00
Stephen D Marshall
c8de2df2cf Sdm qmgrauth (#81)
Implement htpassword changes
2020-03-27 10:09:41 +00:00
Luke Powlett
7f14cc2751 Added SGID to all MQ directories for crtmqdir warning 2020-03-26 15:45:53 +00:00
Luke Powlett
35293e1b46 Build 9.1.5 production image only until developer auth complete 2020-03-24 12:54:58 +00:00
Luke J Powlett
d2bc7b2adc Build a custom MQ package as part of build process 2020-03-24 12:54:58 +00:00
Luke Powlett
f3777a499b Updated to MQ 9.1.5 license 2020-03-24 12:54:58 +00:00
Luke Powlett
f491d23d3b Updated MQ_ARCHIVE names in line with updated MQ 9.1.5.0 naming 2020-03-24 12:54:58 +00:00
Luke Powlett
d4c3fad8c5 Updated runmqdevserver to remove web console config, added group write perms to /etc/mqm/web 2020-03-24 12:54:58 +00:00
Luke J Powlett
d9c8fc5c78 Only build/release P/Z platforms for developer image 2020-03-24 12:54:58 +00:00
Luke J Powlett
c1cbb62ee1 Always print file diagnostics before/after crtmqdir if DEBUG=true 2020-03-24 12:54:58 +00:00
Luke J Powlett
2fae0e2258 Fixed user information logging 2020-03-24 12:54:58 +00:00
Luke J Powlett
c9bac5b544 MQ 9.1.5 image changes (#62)
* Upgraded to MQ 9.1.5, upgraded to unzippable install, run as random UID (1001 by default)

* Updated docker tests for MQ 915 random UID

* Added warning to crtmqdir for 10 rc, added trace option to crtmqdir

* Removed dev users from dockerfile
2020-03-24 12:54:58 +00:00
Luke Powlett
1a7a9236b7 Update to latest UBI version 2020-03-20 10:26:22 +00:00
Stephen D Marshall
6d69355ab9 Tls fix (#74)
* Fix issue with TLS
2020-03-19 12:26:13 +00:00
Luke J Powlett
49b4660360 Updated to latest UBI8/go-toolset versions (#63)
* Updated to latest UBI8/go-toolset versions
2020-03-03 10:02:23 +00:00
Stephen Marshall
ea38c9cd5c Remove seLinuxOption spc_t from README 2020-02-28 16:49:25 +00:00
Luke J Powlett
3ebd64f4da Added ibm- prefix to default image name (#60)
* Added ibm- prefix to default image name
2020-02-20 15:27:40 +00:00
Amrit K Kandola
5e23d979d2 Remove credential helper from Z due to bionic (#59)
* Temporarily remove credential helper from Z build due to bionic issue
2020-02-20 14:19:17 +00:00
Amrit K Kandola
b64f8e8c21 Added credential helper (#51)
* Added credential helper script
* make build only for amd64 and z/os
2020-01-22 16:15:57 +00:00
Nicholas-Daffern
2cbad648b9 Renamed environement variable to MQ_GRACE_PERIOD 2020-01-16 11:17:33 +00:00
Nicholas-Daffern
88bcaaecc3 Added endMQM options and tests 2020-01-16 11:17:33 +00:00
Paras Mamgain
176a023a99 Merge pull request #56 from mq-cloudpak/PM-removing_references_of_old_authorization
removing MQ_WEB_ADMIN_USER vairable
2020-01-13 09:49:05 +05:30
Paras Mamgain
7f7883a312 Merge branch 'private-master' into PM-removing_references_of_old_authorization 2020-01-13 09:01:33 +05:30
mamgainp
84ea13eef2 removing dist from travis yaml file 2020-01-13 09:00:45 +05:30
Luke J Powlett
4cab3e8d3b Added enable trace option to strmqm, Temporarily removing dist tag as not supported by power build (#57)
* Added enable trace option to strmqm

* Temporarily removing dist tag as not supported by power build
2020-01-10 10:03:32 +00:00
mamgainp
98ddca52ca removing invalid comment 2020-01-09 15:58:20 +05:30
mamgainp
3ba37b2b2b undo some changes mqwebauth 2020-01-09 11:44:02 +05:30
mamgainp
b4a3d7d732 removing MQ_WEB_ADMIN_USER vairable 2020-01-08 12:23:31 +05:30
mamgainp
3d5317f3da removing MQ_WEB_ADMIN_USER vairable 2020-01-08 11:06:31 +05:30
mamgainp
5891f170c8 removing MQ_WEB_ADMIN_USER vairable 2020-01-08 10:29:09 +05:30
Luke Powlett
f94d1b8af5 Removed docker store manifests/release process 2020-01-07 09:44:55 +00:00
Stephen Marshall
956b4a8e49 Refactor TLS code 2020-01-06 10:12:58 +00:00
Luke Powlett
ce184408df Fixed makefile gosec info bug 2019-12-23 09:45:34 +00:00
Amrit K Kandola
140db42675 Added docker tests to test the new crtmqm -ii option (#50)
* Added docker tests to test the new crtmqm -ii option, removed the old ini merging code

* Fixed issues with docket tests for ctrqmq -ii

* Removed unneeded logging
2019-12-12 10:56:27 +00:00
Luke J Powlett
28b723d6cf Release changes for dockerhub/store (#48)
* Release changes for dockerhub/store
2019-12-11 11:52:43 +00:00
Luke J Powlett
61c909f551 Override default makefile image name and tag for release (#46)
* Override default makefile image name and tag for release
2019-12-05 18:31:02 +00:00
Luke J Powlett
ffda647cdf Fixed docker images labels (#45) 2019-12-05 16:07:11 +00:00
Luke Powlett
5897d6a644 Update the license information in the readme 2019-12-05 11:44:33 +00:00
Luke J Powlett
34f7a57c5d Pipeline changes for travis release (#43)
* Added changes for travis release process

* Renamed build scripts directory

* Don't run basic build if tag is set

* Pull production images from staging on production-release
2019-12-04 19:45:57 +00:00
Luke Powlett
5b5951ec3c Changed sed for password minlen replacement due to change in UBI8 2019-12-04 10:18:23 +00:00
Amrit K Kandola
b20761cea0 Removed error code logic and replaced with library function (#39)
* Removed error code logic and replaced with library function

* Added the gosecignore comment to prevent gosec failing
2019-12-03 16:13:36 +00:00
Amrit K Kandola
5449622d2a Remove developer listener and remove actions to stop and alter system listener (#41) 2019-12-03 16:13:00 +00:00
Luke J Powlett
dd31fb37c3 Changes for MQ 9.1.4.0 (#37)
* Changes for MQ 9.1.4.0

* Only run p and z builds for production build
2019-12-03 15:58:02 +00:00
Stephen Marshall
2e453f2257 Fix web certificate for dev image 2019-12-03 15:24:05 +00:00
Luke J Powlett
1f4528d597 Pipeline changes for Entitled Registry (#38)
* Added makefile targets/variables to pull mq archive from remote

* Updated makefile and travis build for multi arch dev/prod builds

* Updated build deps to use arch specific dep

* Removed glide for dependency management

* Removed `MQ_SDK_ARCHIVE` download target

* Make ARCH overridable in makefile

* Only run golden path test on Power and Z builds

* Only run gosec on amd64 build

* Increased go to 1.12 in travis
2019-12-03 13:16:30 +00:00
Amrit Kandola
c83aeb17c0 Updated toolset 2019-11-28 11:30:27 +00:00
Amrit K Kandola
5a18280057 Updated UBI to UBI8 (#35)
* Updated UBI to UBI8

* Updated the README to include the redhat login

* Updated to use non-deprecated registry

* Added sed filter into docker file to remove ubi7

* Added automatic login into the makefile based on env variables, also removed nodocs from mq-explorer

* Minor typo fix
2019-11-27 09:32:38 +00:00
Amrit K Kandola
8e380b94f4 Closes #242 - update jmstests to use latest version (#33)
* Closes #242 - update jmstests to use latest version
* added class files to git ignore
2019-10-23 15:59:04 +01:00
Stephen Marshall
ebe8b7a6f0 Add required security settings when creating self-signed certificates 2019-10-21 13:13:00 +01:00
Stephen Marshall
ed4466d934 Remove iFrame support, now using header-as-a-service 2019-10-17 17:31:09 +01:00
Luke Powlett
37601187b2 Removed release from makefile, will be set on internal build only 2019-10-11 10:48:20 +01:00
Luke Powlett
366d406f57 Updated manifest for r3 fix release 2019-10-03 15:06:41 +01:00
Arthur Barr
68ad98fc49 Enable INI merging 2019-09-30 14:37:40 +01:00
Luke Powlett
65edac267e Removed ARGs for go-toolset 2019-09-27 13:20:04 +01:00
Arthur Barr
ad0046ab01 Fix gosec for writeFileIfChanged 2019-09-26 16:47:48 +01:00
Arthur Barr
c3fbc5816e Only write INI file if changed 2019-09-26 16:47:48 +01:00
Luke Powlett
11890d887b Added args for go toolset image and tag, fixed issue with logger 2019-09-23 17:08:40 +01:00
Arthur Barr
ac50d46c78 Don't call INI merging code 2019-09-19 11:03:40 +01:00
Arthur Barr
de298a4aad Don't run dspmqinf before crtmqdir 2019-09-19 09:29:04 +01:00
KIRAN DARBHA
4de32ab8d2 fix for double merge issue
update unit-test data

fix gosec

fix gosec

fix gosec
2019-09-18 16:36:47 +01:00
Arthur Barr
8505579b37 Move some packages to pkg directory 2019-09-17 11:08:42 +01:00
Luke Powlett
7c59d647f5 Added gosec scan to end of travis build, upgraded travis go to v1.11 2019-09-12 16:17:50 +01:00
Luke Powlett
8d8e4e4403 error handling and gosec fixes 2019-09-10 15:22:46 +01:00
Stephen Marshall
52b4eeea32 Fix MIQM 2019-08-30 11:44:45 +01:00
Rob Parker
28291306cb set CipherSpec to ANY_TLS12 and refresh security (#362) 2019-08-29 11:58:51 +01:00
Luke Powlett
4da7c60aee Update to UBI 7.7-98 2019-08-29 11:58:51 +01:00
Arthur Barr
1ae24263ad Temporarily remove Docker 1.12 test in Travis 2019-08-29 11:58:51 +01:00
Arthur Barr
d6248424a1 Use podman build if available 2019-08-29 11:58:51 +01:00
Luke Powlett
455c67ad3d increment release in makefile 2019-08-29 11:58:51 +01:00
Luke Powlett
f6ea588349 release tags in fat manifest 2019-08-06 18:00:33 +01:00
Luke Powlett
60fe631616 specify base image as arg 2019-08-06 11:39:23 +01:00
Luke Powlett
221796f746 build image from specified UBI base tag 2019-08-06 11:39:23 +01:00
KiranDarbha
97941bc87c Add initial support for INI file merging (#349) 2019-08-06 09:17:28 +01:00
LPowlett
b8a7167c4e MQ V9.1.3.0 (#348)
* MQ 9.1.3 changes

* Use crtmqdir -a

* Allow generation of TLS certificate with given hostname

* Remove check for certificate env variable

* Updated manifests and changelog for 913

* Use MQ externals to configure console frame ancestors

* Create /run/mqm

* Go sec fixes

* Set SAN when generating certificates

* Remove image source and commit
2019-07-24 12:01:21 +01:00
LPowlett
dc00231ec1 changelog for v912-UBI (#344)
* changelog for v912-UBI
2019-06-25 10:27:40 +01:00
Rob Parker
8a8ea6c6a9 Merge pull request #336 from mamgainp/windows
Windows
2019-06-13 09:39:10 +01:00
mamgainp
9714d0d513 fixes ifelse ladder 2019-06-12 15:39:41 +01:00
Paras Mamgain
6691438fa1 fixes DOWNLOAD_DIR variable for windows 2019-06-11 15:13:10 +01:00
Paras Mamgain
426c6c3b9a fixes DOWNLOAD_DIR variable for windows 2019-06-11 13:59:38 +01:00
Paras Mamgain
8088cb2902 fixes DOWNLOAD_DIR variable for windows 2019-06-11 13:59:22 +01:00
Rob Parker
a2ce23aa96 Merge pull request #334 from parrobe/capifix
use capicmd for renaming certs
2019-06-10 10:58:24 +01:00
Arthur Barr
95ba16cdb2 Merge branch 'master' into capifix 2019-06-10 10:30:50 +01:00
Rob Parker
83fe77b222 Labels and Licenses for RedHat Certification (#329)
* Add required red hat labels

* Add licenses into correct location for RedHat

* Remove reference to CIP

* PR Comments
2019-06-10 10:29:51 +01:00
Robert Parker
7ab30723cd use capicmd for renaming certs 2019-06-07 16:15:42 +01:00
Stephen Marshall
3f9fc0eaa5 Updates to resolve issues identified by gosec (#330)
* Updates to resolve issues identified by gosec
2019-06-07 11:51:34 +01:00
Arthur Barr
ee4351e55d Remove custom RPM notices file 2019-06-04 15:08:24 +01:00
Rob Parker
ebf55608d7 Merge pull request #324 from parrobe/pwfix
Set password length to meet VA scan requirements
2019-05-31 13:11:40 +01:00
Robert Parker
b3fd5f7562 typos 2019-05-31 11:47:02 +01:00
Robert Parker
3c9ec5f14c Add fix for OIDC error 2019-05-31 11:36:04 +01:00
Robert Parker
81c0b70a6f Set password length to meet VA scan requirements 2019-05-31 10:21:14 +01:00
Robert Parker
a6f307c6b5 Remove openssl 2019-05-30 16:53:27 +01:00
Robert Parker
6f677e2a59 Remove old Integration TLS function 2019-05-30 15:24:48 +01:00
Rob Parker
612fe3a9ec Merge pull request #319 from parrobe/tls
Add TLS Support
2019-05-30 10:40:51 +01:00
Robert Parker
0a9c745d96 PR comments 2019-05-30 10:09:24 +01:00
Robert Parker
b64c060ef4 Refactor TLS code 2019-05-30 09:16:40 +01:00
Robert Parker
8e22763f16 Add TLS Support 2019-05-30 09:16:40 +01:00
Stephen Marshall
40b64e620e Change image tag to amd64 2019-05-30 09:15:45 +01:00
LPowlett
700cc53c07 Handle /var/mqm/ permissions in upgrade to ubi (#316)
* handle /var/mqm/ permissions in upgrade to ubi
2019-05-29 11:40:19 +01:00
Arthur Barr
44d75b169c Remove old icon from README 2019-05-22 16:21:04 +01:00
Rob Parker
c0bf371b9e Merge pull request #314 from parrobe/makefix
Fix makefile targets
2019-05-17 12:53:24 +01:00
Robert Parker
e362644a55 Fix makefile targets 2019-05-17 08:29:32 +01:00
Stephen Marshall
c079c1b60d Merge CIP code 2019-05-16 08:58:02 +01:00
Arthur Barr
4a3bdf3b53 Containerized build 2019-05-10 09:24:36 +01:00
Arthur Barr
5ff269d2e3 Use Universal Base Image 2019-05-10 09:24:36 +01:00
Stephen Marshall
3fb2d3fe61 Fix liveness probe for miqm (#308)
* Fix liveness probe for miqm
2019-05-02 10:16:42 +01:00
Stephen Marshall
6c72c894f7 add multi-instance Queue Managers (#307)
* Initial code to implement multi-instance queue manager

* alter default mqsc to prevent race between listeners on standby startup

* Updates to multi-instance queue manager code

* initial multi instance test

* Multi-instance code improvements

* Multi instance fixes and first test

* configure queue manager

* Add mirror log filtering for mult-instance QMs

* Add log message for multi-instance enabled

* Improvements to container runtime logging

* refactor test

* Test active standby switch

* Improve createQueueManager function

* Test multi instance race

* wait

* multi instance mount tests

* skip race test

* mount tests

* no mount test

* single instance split mount tests

* readiness check

* More updates for handling standby queue manager

* Improve standby checks

* Minor fixes to miqm

* Fix logging of JSON errors

* Update copyrights

* Fix log includes
2019-05-01 14:42:25 +01:00
Rob Parker
63af43f19d Expand redaction system to handle uncommon MQSC cases (#304)
* Expand redaction system to handle uncommon MQSC cases

* add invalidMQSC fixes

* address PR comments
2019-04-17 10:34:29 +01:00
Stephen Marshall
64bb5aed8a Remove debug information 2019-04-12 13:15:02 +01:00
Rob Parker
f05a7d3eaf Merge pull request #300 from sdmarshall79/redact-mqsc-more
Handle multiple-spaces when redacting MQSC output
2019-04-09 15:43:49 +01:00
Stephen Marshall
723fe2b998 Handle multiple-spaces when redacting MQSC output 2019-04-09 15:06:25 +01:00
Stephen Marshall
44d0e0a432 Redact sensitive MQSC logs 2019-04-08 08:49:06 +01:00
LPowlett
33defc0fc9 Handle large mqsc files (#295)
* buffer mqsc input to runmqsc

* imports

* error handling on runmqsc
2019-04-04 16:46:07 +01:00
LPowlett
d69befed71 Version 9.1.2.0 (#294)
* Version 9.1.2.0
2019-03-21 18:25:08 +00:00
Rob Parker
0934289b61 Merge pull request #292 from LPowlett/912-release
912 release manifest update
2019-03-21 16:41:11 +00:00
Luke Powlett
f6231cd51c cert expiry 2019-03-21 16:15:31 +00:00
Luke Powlett
d5f04bc470 copyright notice update 2019-03-21 09:57:59 +00:00
Luke Powlett
d8cbf4566e 9.1.2 manifest update 2019-03-21 09:56:14 +00:00
Rob Parker
493bc7bfd4 Merge pull request #286 from parrobe/gosec
fix gosec failures
2019-03-08 13:36:54 +00:00
Robert Parker
1fa4f6f148 fix gosec failures 2019-03-08 12:59:54 +00:00
Rob Parker
ce664dd654 Merge pull request #285 from parrobe/redistdownload
Fixes for builds
2019-03-08 12:47:11 +00:00
Robert Parker
f8e057a1d6 Fix test breaks from unused code 2019-03-08 11:37:45 +00:00
Robert Parker
efd550822d merge latest from master 2019-03-08 11:13:14 +00:00
Robert Parker
d4df05fd2a Add test fixes and fix mq-explorer build 2019-03-08 11:07:44 +00:00
LPowlett
194b04ac13 Revert MQSC error check (#283)
* Revert MQSC error check
2019-03-08 09:13:26 +00:00
Robert Parker
6848038165 hard code the redist client version 2019-03-06 10:03:38 +00:00
Rob Parker
0d3e177147 Merge pull request #282 from arthurbarr/master
Fix POWER and z/Linux builds
2019-03-06 09:58:15 +00:00
Arthur Barr
13f620f21a Increase test timeouts 2019-03-05 15:08:07 +00:00
Arthur Barr
d4a81741cc Prevent re-download of image during build 2019-03-05 15:07:51 +00:00
Arthur Barr
0047301335 Use Makefile target build-sdk-ex 2019-03-05 11:31:27 +00:00
Arthur Barr
5ef532d2c1 Don't use redist client for Go SDK because of P+Z support 2019-03-05 10:19:21 +00:00
Arthur Barr
281cdc4578 Update RHEL build README 2019-03-05 09:47:58 +00:00
Rob Parker
d68c051104 Merge pull request #280 from parrobe/rhelpackages
Add in system to generate licenses on RHEL
2019-03-04 16:00:49 +00:00
Rob Parker
c5a52e616c Merge branch 'master' into rhelpackages 2019-02-27 15:35:11 +00:00
Arthur Barr
c441de7d26 Fixes for tests on RHEL and test log output 2019-02-27 15:32:34 +00:00
Arthur Barr
a194545f08 Don't add root user into mqm group 2019-02-27 15:32:34 +00:00
Arthur Barr
4f57d1bae2 Tidy up commented code 2019-02-27 15:32:34 +00:00
Arthur Barr
cc0f072908 Change for running as a non-root user (#276)
* Enable running container as mqm

* Fix merge problem

* Don't force root usage

* RHEL image runs as mqm instead of root

* Build on host with SELinux enabled

* Enable building on node in an OpenShift cluster

* Enable running container as mqm

* Fix merge problem

* Don't force root usage

* Merge lastest changes from master

* RHEL image runs as mqm instead of root

* Fix merge issues

* Test changes for non-root

* Make timeout properly, and more non-root test fixes

* Run tests with fewer/no capabilities

* Correct usage docs for non-root

* Add security docs

* Add temporary debug output

* Remove debug code

* Fixes for termination-log

* Allow init container to run as root

* Fixes for CentOS build

* Fixes for RHEL build

* Logging improvements

* Fix Dockerfile RHEL/CentOS build

* Fix bash error

* Make all builds specify UID

* Use redist client for Go SDK

* Inspect image before running tests

* New test for init container

* Log container runtime in runmqdevserver

* Add extra capabilities if using a RHEL image
2019-02-27 15:32:34 +00:00
Robert Parker
d834ac7c9c Add in system to generate licenses on RHEL 2019-02-27 15:06:09 +00:00
Rob Parker
2dbee560fe Merge pull request #272 from parrobe/master
fix dockerfile reference to build explorer
2019-02-12 11:33:20 +00:00
Arthur Barr
17d3238161 Merge branch 'master' into master 2019-02-07 17:07:45 +00:00
Luke Powlett
c08ca2e79f update travis to use Ubuntu 16.04 2019-02-07 16:47:32 +00:00
Rob Parker
84df0e8362 Merge branch 'master' into master 2019-02-07 11:41:51 +00:00
Rob Parker
cc213f429f Merge pull request #273 from arthurbarr/make
Fixes for RHEL build
2019-02-07 11:41:34 +00:00
Rob Parker
c29159dd38 Merge branch 'master' into make 2019-02-07 10:49:02 +00:00
Rob Parker
f345ccf920 Merge branch 'master' into master 2019-02-07 10:48:51 +00:00
Rob Parker
d1b1cfc5d8 Merge pull request #274 from parrobe/golint
Use documented way to install golint
2019-02-07 10:48:39 +00:00
Robert Parker
a19c455ea4 up the copyright date 2019-02-07 10:29:41 +00:00
Robert Parker
694b31d6e8 Use documented way to install golint 2019-02-07 10:09:53 +00:00
Arthur Barr
33f82d76ff Use variable for uid and gid 2019-02-06 16:12:18 +00:00
Arthur Barr
77319629fe Update copyright dates 2019-02-06 16:11:52 +00:00
Arthur Barr
d9c70c48c5 Fix subscription checking logic 2019-02-06 14:23:25 +00:00
Arthur Barr
599f5f4b53 Build on host with SELinux enabled 2019-02-06 13:39:42 +00:00
Arthur Barr
6840a575f9 Fix for building on subscribed and unsubscribed hosts 2019-02-06 13:29:44 +00:00
Robert Parker
1b8c816f57 fix dockerfile reference to build explorer 2019-02-05 15:54:31 +00:00
Arthur Barr
9a8ff9b524 copy sub-directories under /etc/yum.repos.d 2019-02-05 13:53:59 +00:00
Arthur Barr
f3c858184f use correct download files in RHEL build process 2019-02-05 13:51:15 +00:00
LPowlett
43676049b7 RHEL Security Vulnerability Test (#270)
* check for security vulnerabilities on rhel

* import

* check host is red hat

* filepath join

* imports
2019-02-04 10:25:55 +00:00
LPowlett
df6ce917c2 endmqm -r try to reconnect (#268) 2019-01-28 11:03:54 +00:00
Rob Parker
d3eb6e0d3d Merge pull request #267 from arthurbarr/master
RHEL build uses redist client and RHEL minimal
2019-01-25 13:49:46 +00:00
Arthur Barr
2bfdd51a01 RHEL build uses redist client and RHEL minimal 2019-01-24 13:51:10 +00:00
LPowlett
be11b3cda1 Dependancy upgrades (#264)
* dependancy upgrades

* revert dep to 0.4.1

* revert file changes
2019-01-10 14:55:22 +00:00
LPowlett
525ff82fe7 Error checking invalid mqsc commands (#261)
* testRepeatingMqsc

* testInvalidMqsc

* 9.1.1 Makefile

* testRepeatingMqsc

* testInvalidMqsc

* update copyright 2019

* update invalid mqsc error message

* update changelog to reflect MQSC changes
2019-01-10 13:41:43 +00:00
Rob Parker
3e07814bf6 update travis build stages to remove duplicate build and label (#263)
* update travis build stages to remove duplicate build and label

* update copyright to 2019
2019-01-10 10:21:20 +00:00
Rob Parker
b1daacf377 Merge pull request #256 from parrobe/releasetag
Add image tag into info output, add ability to print info output on d…
2018-12-12 13:51:26 +00:00
Robert Parker
9c8b3825be Add image tag into info output, add ability to print info output on demand and prevent multiple instances of runmqserver being run 2018-12-12 13:11:47 +00:00
Rob Parker
4145f077b6 update perl-base to fix security vulnerability (#253) 2018-12-05 13:01:31 +00:00
Robert Parker
c063ddd67d remove deprecated tag manifest 2018-12-03 11:31:24 +00:00
508 changed files with 113019 additions and 2858 deletions

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ vendor/github.com/prometheus/client_model/.classpath
vendor/github.com/prometheus/client_model/.project vendor/github.com/prometheus/client_model/.project
vendor/github.com/prometheus/client_model/.settings* vendor/github.com/prometheus/client_model/.settings*
gosec_results.json gosec_results.json
internal/qmgrauth/qmgroam/patch

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2018, 2020
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,62 +12,77 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# Temporarily removing dist tag as not supported by power build
# dist: xenial
sudo: required sudo: required
language: go language: go
go: go:
- "1.10" - "1.13.15"
services: services:
- docker - docker
go_import_path: "github.com/ibm-messaging/mq-container" go_import_path: "github.com/ibm-messaging/mq-container"
cache: # cache:
directories: # directories:
- downloads # - downloads
env: env:
- BASE_IMAGE=ubuntu:16.04 global:
- BASE_IMAGE=centos:latest - RELEASE="r2"
jobs: jobs:
include: include:
- if: type IN (pull_request) - stage: basic-build
env: DOCKER_DOWNGRADE="docker save -o images.tar mqadvanced-server-dev mq-dev-jms-test && if: branch != private-master AND tag IS blank
sudo apt-get autoremove -y docker-ce && name: "Basic AMD64 build"
curl -fsSL \"https://apt.dockerproject.org/gpg\" | sudo apt-key add - && os: linux
sudo apt-add-repository \"deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -cs) main\" && env:
sudo apt-get update && - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_AMD64
sudo apt-get install docker-engine=1.12.6-0~ubuntu-$(lsb_release -cs) && script: bash -e travis-build-scripts/run.sh
docker load -q -i images.tar && - stage: build
export DOCKER_API_VERSION=\"1.24\"" if: branch = private-master OR tag =~ ^release-candidate*
- env: DOCKER_DOWNGRADE="echo nothing to be done" name: "Multi-Arch AMD64 build"
os: linux
env:
- BUILD_ALL=true
- MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_AMD64
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_AMD64
script: bash -e travis-build-scripts/run.sh
# - if: branch = private-master OR tag =~ ^release-candidate*
# name: "Multi-Arch PPC64LE build"
# os: linux-ppc64le
# env:
# - BUILD_ALL=true
# - TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
# # - MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_PPC64LE
# - MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_PPC64LE
# script: bash -e travis-build-scripts/run.sh
- stage: build
if: branch = private-master OR tag =~ ^release-candidate*
name: "Multi-Arch S390X build"
os: linux-s390
env:
- BUILD_ALL=true
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
- MQ_ARCHIVE_REPOSITORY=$MQ_920_ARCHIVE_REPOSITORY_S390X
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_920_ARCHIVE_REPOSITORY_DEV_S390X
script: bash -e travis-build-scripts/run.sh
- stage: push-manifest
if: branch = private-master OR tag =~ ^release-candidate*
name: "Push Manifest-list to registry"
script: make push-manifest
before_install: before_install:
- ./install-build-deps-ubuntu.sh - make install-build-deps
- make install-credential-helper
install: install:
- echo nothing - echo nothing
before_script: before_script: echo nothing
- echo 'Downloading Go dependencies...' && echo -en 'travis_fold:start:deps\\r'
- make deps
- echo -en 'travis_fold:end:deps\\r'
- echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r'
- make build-devserver
- echo -en 'travis_fold:end:build-devserver\\r'
- echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r'
- make build-devjmstest
- echo -en 'travis_fold:end:build-devjmstest\\r'
script:
- echo 'Downgrading Docker (if necessary)...' && echo -en 'travis_fold:start:docker-downgrade\\r'
- eval "$DOCKER_DOWNGRADE"
- echo -en 'travis_fold:end:docker-downgrade\\r'
- echo 'Testing Developer image...' && echo -en 'travis_fold:start:test-devserver\\r'
- make test-devserver
- echo -en 'travis_fold:end:test-devserver\\r'
after_success: after_success:
- make lint - make lint

View File

@@ -1,11 +1,61 @@
# Change log # Change log
## 9.2.0.0 (2020-07-23)
* Updated to [MQ version 9.2.0.0](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.pro.doc/q113110_.htm)
* Use `-ic` arguments with `crtmqm` to process MQSC files in `/etc/mqm`. Replaces previous use of "runmqsc" commands
## 9.1.5.0 (2020-04-02)
* Updated to MQ version 9.1.5.0
* Can now run as a random user, instead of the "mqm" user, which has now been removed. This adds compatability for the [Red Hat OpenShift restricted SCC](https://docs.openshift.com/container-platform/4.3/authentication/managing-security-context-constraints.html#security-context-constraints-about_configuring-internal-oauth). The default image UID is `1001`.
## 9.1.4.0 (2019-12-06)
* Updated to MQ version 9.1.4.0
* Updated to use UBI8 as base image
* Added required security settings to self signed certificates to align with macOS Catalina requirements
## 9.1.3.0 (2019-07-19)
* Updated to MQ version 9.1.3.0
* Allow generation of TLS certificate with given hostname
* Fixes for the following issues:
* `MQ_EPHEMERAL_PREFIX` UNIX sockets fix
* Fix Makefile for Windows
* Use -a option on crtmqdir
* Remove check for certificate environment variable
## 9.1.2.0-UBI (2019-06-21)
**Breaking changes**:
* UID of the mqm user is now 888. You need to run the container with an entrypoint of `runmqserver -i` under the root user to update any existing files.
* MQSC files supplied will be verified before being run. Files containing invalid MQSC will cause the container to fail to start
**Other changes**:
* Security fixes
* Web console added to production image
* Container built on RedHat host
## 9.1.2.0 (2019-03-21)
* Updated to MQ version 9.1.2.0
* Now runs using the "mqm" user instead of root. See new [security doc](https://github.com/ibm-messaging/mq-container/blob/master/docs/security.md)
* New [IGNSTATE](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.pro.doc/q132310_.htm#q132310___ignstateparm) parameter used in default developer config
* Termination log moved from `/dev/termination-log` to `/run/termination-log`, to make permissions easier to handle
* Fixes for the following issues:
* Brackets no longer appear in termination log
* Test timeouts weren't being used correctly
* Building on subscribed and unsubscribed hosts ([#273](https://github.com/ibm-messaging/mq-container/pull/273))
* Gosec failures ([#286](https://github.com/ibm-messaging/mq-container/pull/286))
* Security fix for perl-base ([#253](https://github.com/ibm-messaging/mq-container/pull/253))
## 9.1.1.0 (2018-11-30) ## 9.1.1.0 (2018-11-30)
* Updated to MQ version 9.1.1.0 * Updated to MQ version 9.1.1.0
* Created seperate RedHat Makefile for building images on RedHat machines with buildah * Created seperate RedHat Makefile for building images on RedHat machines with buildah
* Enabled REST messaging capability for app user. * Enabled REST messaging capability for app user.
* Added support for container suplimentary groups * Added support for container supplementary groups
* Removed IBM MQ version 9.0.5 details. * Removed IBM MQ version 9.0.5 details.
* Added additional Diagnostics ([#203](https://github.com/ibm-messaging/mq-container/pull/203)) * Added additional Diagnostics ([#203](https://github.com/ibm-messaging/mq-container/pull/203))
* Implementted GOSec to perform code scans for security vulnerabilities. (([#227](https://github.com/ibm-messaging/mq-container/pull/227))) * Implementted GOSec to perform code scans for security vulnerabilities. (([#227](https://github.com/ibm-messaging/mq-container/pull/227)))

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2018 # © Copyright IBM Corporation 2015, 2020
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,63 +12,152 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
ARG BASE_IMAGE=ubuntu:16.04 ARG BASE_IMAGE=registry.redhat.io/ubi8/ubi-minimal
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.1.0-x86_64-ubuntu-16.04 ARG BASE_TAG=8.2-349
ARG GO_WORKDIR=/go/src/github.com/ibm-messaging/mq-container
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.2.0.0-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
############################################################################### ###############################################################################
# Build stage to build Go code # Build stage to build Go code
############################################################################### ###############################################################################
FROM $BUILDER_IMAGE as builder FROM golang:1.13.15 as builder
WORKDIR /go/src/github.com/ibm-messaging/mq-container/ # The URL to download the MQ installer from in tar.gz format
# This assumes an archive containing the MQ Non-Install packages
ARG MQ_URL
ARG IMAGE_REVISION="Not specified" ARG IMAGE_REVISION="Not specified"
ARG IMAGE_SOURCE="Not specified" ARG IMAGE_SOURCE="Not specified"
ARG IMAGE_TAG="Not specified"
ARG GO_WORKDIR
USER 0
COPY install-mq.sh /usr/local/bin/
RUN mkdir /opt/mqm \
&& chmod a+x /usr/local/bin/install-mq.sh \
&& sleep 1 \
&& INSTALL_SDK=1 install-mq.sh \
&& chown -R 1001:root /opt/mqm/*
WORKDIR $GO_WORKDIR/
COPY cmd/ ./cmd COPY cmd/ ./cmd
COPY internal/ ./internal COPY internal/ ./internal
COPY pkg/ ./pkg
COPY vendor/ ./vendor COPY vendor/ ./vendor
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" ./cmd/runmqserver/ ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*"
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/
RUN go build ./cmd/chkmqready/ RUN go build ./cmd/chkmqready/
RUN go build ./cmd/chkmqhealthy/ RUN go build ./cmd/chkmqhealthy/
# Run all unit tests RUN go build ./cmd/runmqdevserver/
RUN go build -buildmode=c-shared -o amqpasdev.so ./internal/qmgrauth/pas.go
RUN go test -v ./cmd/runmqdevserver/...
RUN go test -v ./cmd/runmqserver/ RUN go test -v ./cmd/runmqserver/
RUN go test -v ./cmd/chkmqready/ RUN go test -v ./cmd/chkmqready/
RUN go test -v ./cmd/chkmqhealthy/ RUN go test -v ./cmd/chkmqhealthy/
RUN go test -v ./pkg/...
RUN go test -v ./internal/... RUN go test -v ./internal/...
RUN go vet ./cmd/... ./internal/... RUN go vet ./cmd/... ./internal/...
############################################################################### ###############################################################################
# Main build stage, to build MQ image # Main build stage, to build MQ image
############################################################################### ###############################################################################
FROM $BASE_IMAGE FROM $BASE_IMAGE:$BASE_TAG AS mq-server
# The URL to download the MQ installer from in tar.gz format
# This assumes an archive containing the MQ Debian (.deb) install packages
ARG MQ_URL
# The MQ packages to install - see install-mq.sh for default value # The MQ packages to install - see install-mq.sh for default value
ARG MQ_PACKAGES ARG MQ_URL
ARG BASE_IMAGE
ARG BASE_TAG
ARG GO_WORKDIR
LABEL summary="IBM MQ Advanced Server"
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises"
LABEL vendor="IBM"
LABEL maintainer="IBM"
LABEL distribution-scope="private"
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
LABEL url="https://www.ibm.com/products/mq/advanced"
LABEL io.openshift.tags="mq messaging"
LABEL io.k8s.display-name="IBM MQ Advanced Server"
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises"
LABEL base-image=$BASE_IMAGE
LABEL base-image-release=$BASE_TAG
COPY install-mq.sh /usr/local/bin/ COPY install-mq.sh /usr/local/bin/
COPY install-mq-server-prereqs.sh /usr/local/bin/
# Install MQ. To avoid a "text file busy" error here, we sleep before installing. # Install MQ. To avoid a "text file busy" error here, we sleep before installing.
RUN chmod u+x /usr/local/bin/install-mq.sh \ RUN env \
&& mkdir /opt/mqm \
&& chmod u+x /usr/local/bin/install-*.sh \
&& sleep 1 \ && sleep 1 \
&& install-mq.sh && install-mq-server-prereqs.sh \
&& install-mq.sh \
&& /opt/mqm/bin/security/amqpamcf \
&& chown -R 1001:root /opt/mqm/*
# Create a directory for runtime data from runmqserver # Create a directory for runtime data from runmqserver
RUN mkdir -p /run/runmqserver \ RUN mkdir -p /run/runmqserver \
&& chown mqm:mqm /run/runmqserver && chown 1001:root /run/runmqserver
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/ COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/chkmq* /usr/local/bin/
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
# Copy web XML files
COPY web /etc/mqm/web
COPY etc/mqm/*.tpl /etc/mqm/
RUN chmod ug+x /usr/local/bin/runmqserver \ RUN chmod ug+x /usr/local/bin/runmqserver \
&& chown mqm:mqm /usr/local/bin/*mq* \ && chown 1001:root /usr/local/bin/*mq* \
&& chmod ug+xs /usr/local/bin/chkmq* && chmod ug+xs /usr/local/bin/chkmq* \
&& chown -R 1001:root /etc/mqm/* \
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
&& touch /run/termination-log \
&& chown 1001:root /run/termination-log \
&& chmod 0660 /run/termination-log \
&& chmod -R g+w /etc/mqm/web
# Always use port 1414 for MQ & 9157 for the metrics # Always use port 1414 for MQ & 9157 for the metrics
EXPOSE 1414 9157 EXPOSE 1414 9157 9443
ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin"
ENV MQ_GRACE_PERIOD=30
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
# We can run as any UID
USER 1001
ENV MQ_CONNAUTH_USE_HTP=false
ENTRYPOINT ["runmqserver"] ENTRYPOINT ["runmqserver"]
###############################################################################
# Add default developer config
###############################################################################
FROM mq-server AS mq-dev-server
ARG BASE_IMAGE
ARG BASE_TAG
ARG GO_WORKDIR
# Enable MQ developer default configuration
ENV MQ_DEV=true
LABEL summary="IBM MQ Advanced for Developers Server"
LABEL description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises"
LABEL vendor="IBM"
LABEL distribution-scope="private"
LABEL authoritative-source-url="https://www.ibm.com/software/passportadvantage/"
LABEL url="https://www.ibm.com/products/mq/advanced"
LABEL io.openshift.tags="mq messaging"
LABEL io.k8s.display-name="IBM MQ Advanced for Developers Server"
LABEL io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the worlds most successful enterprises"
LABEL base-image=$BASE_IMAGE
LABEL base-image-release=$BASE_TAG
USER 0
COPY --from=builder $GO_WORKDIR/amqpas* /opt/mqm/lib64/
COPY etc/mqm/*.ini /etc/mqm/
COPY etc/mqm/mq.htpasswd /etc/mqm/
RUN chmod 0660 /etc/mqm/mq.htpasswd
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
&& sleep 1 \
&& install-extra-packages.sh
# Create a directory for runtime data from runmqserver
RUN mkdir -p /run/runmqdevserver \
&& chown 1001:root /run/runmqdevserver
COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
# Copy template files
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
# Copy web XML files for default developer configuration
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
RUN chown -R 1001:root /etc/mqm/* \
&& chmod -R g+w /etc/mqm/web \
&& chmod +x /usr/local/bin/runmq* \
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
ENV MQ_ENABLE_EMBEDDED_WEB_SERVER=1 MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost
ENV LD_LIBRARY_PATH=/opt/mqm/lib64
ENV MQS_PERMIT_UNKNOWN_ID=true
ENV MQ_CONNAUTH_USE_HTP=true
USER 1001
ENTRYPOINT ["runmqdevserver"]

View File

@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
© Copyright IBM Corporation. 2015, 2018 © Copyright IBM Corporation. 2015, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

417
Makefile
View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2017, 2020
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -13,115 +13,384 @@
# limitations under the License. # limitations under the License.
############################################################################### ###############################################################################
# Variables # Conditional variables - you can override the values of these variables from
# the command line
###############################################################################
# RELEASE shows what release of the container code has been built
RELEASE ?=
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.2.0.0
# MQ_ARCHIVE_REPOSITORY is a remote repository from which to pull the MQ_ARCHIVE (if required)
MQ_ARCHIVE_REPOSITORY ?=
# MQ_ARCHIVE_REPOSITORY_DEV is a remote repository from which to pull the MQ_ARCHIVE_DEV (if required)
MQ_ARCHIVE_REPOSITORY_DEV ?=
# MQ_ARCHIVE_REPOSITORY_USER is the user for the remote repository (if required)
MQ_ARCHIVE_REPOSITORY_USER ?=
# MQ_ARCHIVE_REPOSITORY_CREDENTIAL is the password/API key for the remote repository (if required)
MQ_ARCHIVE_REPOSITORY_CREDENTIAL ?=
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
# be installed. Does not apply to MQ Advanced for Developers
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH)_NOINST.tar.gz
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
# for Developers can be installed
MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_ARCHIVE_DEV_TYPE)$(MQ_ARCHIVE_DEV_ARCH).tar.gz
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# Options to `go test` for the Docker tests
TEST_OPTS_DOCKER ?=
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
MQ_IMAGE_DEVSERVER ?=ibm-mqadvanced-server-dev
# MQ_TAG is the tag of the built MQ Advanced image & MQ Advanced for Developers image
MQ_TAG ?=$(MQ_VERSION)-$(ARCH)
# COMMAND is the container command to run. "podman" or "docker"
COMMAND ?=$(shell type -p podman 2>&1 >/dev/null && echo podman || echo docker)
# MQ_DELIVERY_REGISTRY_HOSTNAME is a remote registry to push the MQ Image to (if required)
MQ_DELIVERY_REGISTRY_HOSTNAME ?=
# MQ_DELIVERY_REGISTRY_NAMESPACE is the namespace/path on the delivery registry (if required)
MQ_DELIVERY_REGISTRY_NAMESPACE ?=
# MQ_DELIVERY_REGISTRY_USER is the user for the remote registry (if required)
MQ_DELIVERY_REGISTRY_USER ?=
# MQ_DELIVERY_REGISTRY_CREDENTIAL is the password/API key for the remote registry (if required)
MQ_DELIVERY_REGISTRY_CREDENTIAL ?=
# REGISTRY_USER is the username used to login to the Red Hat registry
REGISTRY_USER ?=
# REGISTRY_PASS is the password used to login to the Red Hat registry
REGISTRY_PASS ?=
# ARCH is the platform architecture (e.g. amd64, ppc64le or s390x)
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m))
# Tag to use for fat-manifest
MQ_MANIFEST_TAG=$(MQ_VERSION)
###############################################################################
# Other variables
############################################################################### ###############################################################################
GO_PKG_DIRS = ./cmd ./internal ./test GO_PKG_DIRS = ./cmd ./internal ./test
MQ_ARCHIVE_TYPE=LINUX
MQ_ARCHIVE_DEV_TYPE=Linux
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
BUILD_SERVER_CONTAINER=build-server
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
# test run in parallel
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base
# Docker image name to use for JMS tests
DEV_JMS_IMAGE=mq-dev-jms-test
# Variables for versioning
IMAGE_REVISION=$(shell git rev-parse HEAD)
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
EMPTY:=
SPACE:= $(EMPTY) $(EMPTY)
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.2.0 instead of 9.2.0.0
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
# Set variable if running on a Red Hat Enterprise Linux host ifneq (,$(findstring Microsoft,$(shell uname -r)))
ifneq ($(wildcard /etc/redhat-release),) DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
REDHAT_RELEASE = $(shell cat /etc/redhat-release) else ifneq (,$(findstring Windows,$(shell echo ${OS})))
ifeq "$(findstring Red Hat,$(REDHAT_RELEASE))" "Red Hat" DOWNLOADS_DIR=$(shell pwd)/downloads/
RHEL_HOST = "true" else
DOWNLOADS_DIR=$(realpath ./downloads/)
endif endif
# Try to figure out which archive to use from the architecture
ifeq "$(ARCH)" "amd64"
MQ_ARCHIVE_ARCH=X86-64
MQ_ARCHIVE_DEV_ARCH=X64
else ifeq "$(ARCH)" "ppc64le"
MQ_ARCHIVE_ARCH=PPC64LE
else ifeq "$(ARCH)" "s390x"
MQ_ARCHIVE_ARCH=S390X
endif endif
ifneq "$(MQ_DELIVERY_REGISTRY_NAMESPACE)" "$(EMPTY)"
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE)
else
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)
endif
ifneq "$(RELEASE)" "$(EMPTY)"
MQ_TAG=$(MQ_VERSION)-$(RELEASE)-$(ARCH)
EXTRA_LABELS=--label release=$(RELEASE)
MQ_MANIFEST_TAG=$(MQ_VERSION)-$(RELEASE)
endif
MQ_IMAGE_FULL_RELEASE_NAME=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
MQ_IMAGE_DEV_FULL_RELEASE_NAME=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
#setup variables for fat-manifests
MQ_IMAGE_DEVSERVER_MANIFEST=$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)
MQ_IMAGE_ADVANCEDSERVER_MANIFEST=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)
MQ_IMAGE_DEVSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)-amd64
MQ_IMAGE_DEVSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)-s390x
MQ_IMAGE_ADVANCEDSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)-amd64
MQ_IMAGE_ADVANCEDSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)-s390x
############################################################################### ###############################################################################
# Build targets # Build targets
############################################################################### ###############################################################################
.PHONY: default
default: build-devserver
# Targets default to a RHEL image on a RHEL host, or an Ubuntu image everywhere else # Build all components (except incubating ones)
.PHONY: all
all: build-devserver build-advancedserver
.PHONY: build-devserver .PHONY: test-all
ifdef RHEL_HOST test-all: build-devjmstest test-devserver test-advancedserver
build-devserver: build-devserver-rhel
.PHONY: devserver
devserver: build-devserver build-devjmstest test-devserver
.PHONY: advancedserver
advancedserver: build-advancedserver test-advancedserver
# Build incubating components
.PHONY: incubating
incubating: build-explorer
downloads/$(MQ_ARCHIVE_DEV):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads
ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)"
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" -o downloads/$(MQ_ARCHIVE_DEV)
else else
build-devserver: build-devserver-ubuntu curl -L https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV) -o downloads/$(MQ_ARCHIVE_DEV)
endif endif
.PHONY: build-advancedserver downloads/$(MQ_ARCHIVE):
ifdef RHEL_HOST $(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced "$(MQ_VERSION)$(END)))
build-advancedserver: build-advancedserver-rhel mkdir -p downloads
else ifneq "$(MQ_ARCHIVE_REPOSITORY)" "$(EMPTY)"
build-advancedserver: build-advancedserver-ubuntu curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY)" -o downloads/$(MQ_ARCHIVE)
endif endif
.PHONY: downloads
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
.PHONY: test-devserver # Vendor Go dependencies for the Docker tests
ifdef RHEL_HOST test/docker/vendor:
test-devserver: test-devserver-rhel cd test/docker && dep ensure -vendor-only
else
test-devserver: test-devserver-ubuntu # Shortcut to just run the unit tests
endif .PHONY: test-unit
test-unit:
docker build --target builder --file Dockerfile-server .
.PHONY: test-advancedserver .PHONY: test-advancedserver
ifdef RHEL_HOST test-advancedserver: test/docker/vendor
test-advancedserver: test-advancedserver-rhel $(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
else docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
test-advancedserver: test-advancedserver-ubuntu cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
endif
.PHONY: build-devjmstest .PHONY: build-devjmstest
ifdef RHEL_HOST build-devjmstest:
build-devjmstest: build-devjmstest-rhel $(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
else cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
build-devjmstest: build-devjmstest-ubuntu
.PHONY: test-devserver
test-devserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
.PHONY: coverage
coverage:
mkdir coverage
.PHONY: test-advancedserver-cover
test-advancedserver-cover: test/docker/vendor coverage
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell docker --version)"$(END)))
rm -f ./coverage/unit*.cov
# Run unit tests with coverage, for each package under 'internal'
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
echo 'mode: count' > ./coverage/unit.cov
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
rm -f ./test/docker/coverage/*.cov
rm -f ./coverage/docker.*
mkdir -p ./test/docker/coverage/
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
echo 'mode: count' > ./coverage/docker.cov
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
echo 'mode: count' > ./coverage/combined.cov
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
# Build an MQ image. The commands used are slightly different between Docker and Podman
define build-mq
$(if $(findstring docker,$(COMMAND)), @docker network create build,)
$(if $(findstring docker,$(COMMAND)), @docker run --rm --name $(BUILD_SERVER_CONTAINER) --network build --network-alias build --volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro --detach docker.io/nginx:alpine,)
$(eval EXTRA_ARGS=$(if $(findstring docker,$(COMMAND)), --network build --build-arg MQ_URL=http://build:80/$4, --volume $(DOWNLOADS_DIR):/var/downloads --build-arg MQ_URL=file:///var/downloads/$4))
# Build the new image
$(COMMAND) build \
--tag $1:$2 \
--file $3 \
$(EXTRA_ARGS) \
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
--build-arg IMAGE_TAG="$1:$2" \
--label version=$(MQ_VERSION) \
--label name=$1 \
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
--label architecture="$(ARCH)" \
--label run="docker run -d -e LICENSE=accept $1:$2" \
--label vcs-ref=$(IMAGE_REVISION) \
--label vcs-type=git \
--label vcs-url=$(IMAGE_SOURCE) \
$(EXTRA_LABELS) \
--target $5 \
.
$(if $(findstring docker,$(COMMAND)), @docker kill $(BUILD_SERVER_CONTAINER))
$(if $(findstring docker,$(COMMAND)), @docker network rm build)
endef
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
PODMAN_VERSION=$(shell podman version --format "{{ .Version }}")
.PHONY: command-version
command-version:
# If we're using Docker, then check it's recent enough to support multi-stage builds
ifneq (,$(findstring docker,$(COMMAND)))
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
endif
ifneq (,$(findstring podman,$(COMMAND)))
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "1" || (echo "Error: Podman version 1.0 or greater is required" && exit 1)
endif endif
# UBUNTU building targets .PHONY: build-advancedserver-host
.PHONY: build-devserver-ubuntu build-advancedserver-host: build-advancedserver
build-devserver-ubuntu:
$(MAKE) -f Makefile-UBUNTU build-devserver
.PHONY: test-devserver-ubuntu .PHONY: build-advancedserver
test-devserver-ubuntu: build-advancedserver: registry-login log-build-env downloads/$(MQ_ARCHIVE) command-version
$(MAKE) -f Makefile-UBUNTU test-devserver $(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
.PHONY: build-devjmstest-ubuntu .PHONY: build-devserver-host
$(MAKE) -f Makefile-UBUNTU build-devjmstest build-devserver-host: build-devserver
.PHONY: build-advancedserver-ubuntu .PHONY: build-devserver
build-advancedserver-ubuntu: build-devserver: registry-login log-build-env downloads/$(MQ_ARCHIVE_DEV) command-version
$(MAKE) -f Makefile-UBUNTU build-advancedserver $(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
.PHONY: test-advancedserver-ubuntu .PHONY: build-advancedserver-cover
test-advancedserver-ubuntu: build-advancedserver-cover: registry-login command-version
$(MAKE) -f Makefile-UBUNTU test-advancedserver $(COMMAND) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) -t $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover -f Dockerfile-server.cover .
.PHONY: build-devjmstest-ubuntu .PHONY: build-explorer
build-devjmstest-ubuntu: build-explorer: registry-login downloads/$(MQ_ARCHIVE_DEV)
$(MAKE) -f Makefile-UBUNTU build-devjmstest $(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
# RHEL building targets .PHONY: build-sdk
.PHONY: build-devserver-rhel build-sdk: registry-login downloads/$(MQ_ARCHIVE_DEV)
build-devserver-rhel: $(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
$(MAKE) -f Makefile-RHEL build-devserver $(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
.PHONY: test-devserver-rhel .PHONY: registry-login
test-devserver-rhel: registry-login:
$(MAKE) -f Makefile-RHEL test-devserver ifneq ($(REGISTRY_USER),)
$(COMMAND) login -u $(REGISTRY_USER) -p $(REGISTRY_PASS) registry.redhat.io
endif
.PHONY: build-advancedserver-rhel .PHONY: log-build-env
build-advancedserver-rhel: log-build-vars:
$(MAKE) -f Makefile-RHEL build-advancedserver $(info $(SPACER)$(shell printf $(TITLE)"Build environment"$(END)))
@echo ARCH=$(ARCH)
@echo MQ_VERSION=$(MQ_VERSION)
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
@echo MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV)
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
@echo COMMAND=$(COMMAND)
@echo REGISTRY_USER=$(REGISTRY_USER)
.PHONY: test-advancedserver-rhel .PHONY: log-build-env
test-advancedserver-rhel: log-build-env: log-build-vars
$(MAKE) -f Makefile-RHEL test-advancedserver $(info $(SPACER)$(shell printf $(TITLE)"Build environment - $(COMMAND) info"$(END)))
@echo Command version: $(shell $(COMMAND) --version)
$(COMMAND) info
.PHONY: build-devjmstest-rhel include formatting.mk
build-devjmstest-rhel:
$(MAKE) -f Makefile-RHEL build-devjmstest .PHONY: pull-mq-archive
pull-mq-archive:
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY)" -o downloads/$(MQ_ARCHIVE)
.PHONY: pull-mq-archive-dev
pull-mq-archive-dev:
curl -u $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -X GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" -o downloads/$(MQ_ARCHIVE_DEV)
.PHONY: push-advancedserver
push-advancedserver:
$(info $(SPACER)$(shell printf $(TITLE)"Push production image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
$(COMMAND) tag $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
$(COMMAND) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
.PHONY: push-devserver
push-devserver:
$(info $(SPACER)$(shell printf $(TITLE)"Push developer image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
$(COMMAND) tag $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
$(COMMAND) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
.PHONY: pull-advancedserver
pull-advancedserver:
$(info $(SPACER)$(shell printf $(TITLE)"Pull production image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME) $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG)
.PHONY: pull-devserver
pull-devserver:
$(info $(SPACER)$(shell printf $(TITLE)"Pull developer image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME) $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG)
.PHONY: push-manifest
push-manifest: build-skopeo-container
$(info $(SPACER)$(shell printf $(TITLE)"** Determining the image digests **"$(END)))
$(eval MQ_IMAGE_DEVSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux --override-arch s390x inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_AMD64) | jq -r .Digest))
$(eval MQ_IMAGE_DEVSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_S390X) | jq -r .Digest))
$(eval MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_AMD64) | jq -r .Digest))
$(eval MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_S390X) | jq -r .Digest))
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_AMD64) has a digest of $(MQ_IMAGE_DEVSERVER_AMD64_DIGEST)**"$(END)))
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_S390X) has a digest of $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)**"$(END)))
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_AMD64) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST)**"$(END)))
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_S390X) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)**"$(END)))
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_DEVSERVER_MANIFEST)**"$(END)))
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_DEVSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_DEVSERVER_AMD64_DIGEST) $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)" $(END))
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_ADVANCEDSERVER_MANIFEST)**"$(END)))
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)" $(END))
.PHONY: build-skopeo-container
build-skopeo-container:
$(COMMAND) images | grep -q "skopeo"; if [ $$? != 0 ]; then docker build -t skopeo:latest ./docker-builds/skopeo/; fi
# Common targets
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf ./coverage rm -rf ./coverage
rm -rf ./build rm -rf ./build
rm -rf ./deps rm -rf ./deps
.PHONY: deps .PHONY: install-build-deps
deps: install-build-deps:
glide install --strip-vendor ARCH=$(ARCH) ./install-build-deps.sh
.PHONY: install-credential-helper
install-credential-helper:
ifeq ($(ARCH),amd64)
ARCH=$(ARCH) ./travis-build-scripts/install-credential-helper.sh
endif
.PHONY: build-cov .PHONY: build-cov
build-cov: build-cov:
@@ -142,7 +411,8 @@ lint: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
golint -set_exit_status $(sort $(dir $(wildcard $(addsuffix /*/*.go, $(GO_PKG_DIRS))))) golint -set_exit_status $(sort $(dir $(wildcard $(addsuffix /*/*.go, $(GO_PKG_DIRS)))))
.PHONY: gosec .PHONY: gosec
gosec: $(info $(SPACER)$(shell printf "Running gosec test"$(END))) gosec:
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
@gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null ;\ @gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null ;\
cat "gosec_results.json" ;\ cat "gosec_results.json" ;\
cat gosec_results.json | grep HIGH | grep severity > /dev/null ;\ cat gosec_results.json | grep HIGH | grep severity > /dev/null ;\
@@ -167,9 +437,4 @@ gosec: $(info $(SPACER)$(shell printf "Running gosec test"$(END)))
printf "\ngosec found no LOW severity issues\n" ;\ printf "\ngosec found no LOW severity issues\n" ;\
fi ;\ fi ;\
.PHONY: unknownos
unknownos:
$(info $(SPACER)$(shell printf "ERROR: Unknown OS ("$(BASE_OS)") please run specific make targets"$(END)))
exit 1
include formatting.mk include formatting.mk

View File

@@ -1,197 +0,0 @@
# © Copyright IBM Corporation 2018
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
# Conditional variables - you can override the values of these variables from
# the command line
###############################################################################
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
BASE_IMAGE ?= rhel
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.1.1.0
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
# Does not apply to MQ Advanced for Developers.
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_LINUX_$(MQ_ARCHIVE_ARCH).tar.gz
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
# for Developers can be installed
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# Options to `go test` for the Docker tests
TEST_OPTS_DOCKER ?=
# MQ_IMAGE_ADVANCEDSERVER is the name and tag of the built MQ Advanced image
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-RHEL-$(ARCH)
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-RHEL-$(ARCH)
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# MQ_PACKAGES specifies the MQ packages to install. Defaults vary on base image.
MQ_PACKAGES ?= MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm
###############################################################################
# Other variables
###############################################################################
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
ARCH = $(shell uname -m)
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# Docker image name to use for JMS tests
DEV_JMS_IMAGE=mq-dev-jms-test:latest
# Variables for versioning
IMAGE_REVISION=$(shell git rev-parse HEAD)
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
MQDEV=
ifneq (,$(findstring Microsoft,$(shell uname -r)))
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
else
DOWNLOADS_DIR=$(realpath ./downloads/)
endif
# Try to figure out which archive to use from the architecture
ifeq "$(ARCH)" "x86_64"
MQ_ARCHIVE_ARCH=X86-64
MQ_DEV_ARCH=x86-64
else ifeq "$(ARCH)" "ppc64le"
MQ_ARCHIVE_ARCH=LE_POWER
MQ_DEV_ARCH=ppcle
else ifeq "$(ARCH)" "s390x"
MQ_ARCHIVE_ARCH=SYSTEM_Z
MQ_DEV_ARCH=s390x
endif
# Archive names for IBM MQ Advanced for Developers
MQ_ARCHIVE_DEV_9.0.5.0=mqadv_dev905_linux_x86-64.tar.gz
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_linux_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev910_linux_$(MQ_DEV_ARCH).tar.gz
###############################################################################
# Build targets
###############################################################################
.PHONY: vars
vars:
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
@echo $(MQ_ARCHIVE_ARCH)
@echo $(MQ_ARCHIVE_TYPE)
@echo $(MQ_ARCHIVE)
.PHONY: default
default: build-devserver test-devserver
# Build all components (except incubating ones)
.PHONY: all
all: build-devserver build-advancedserver
.PHONY: test-all
test-all: build-devjmstest test-devserver test-advancedserver
.PHONY: devserver
devserver: build-devserver build-devjmstest test-devserver
# Build incubating components
.PHONY: incubating
incubating: build-explorer
downloads/$(MQ_ARCHIVE_DEV):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
downloads/$(MQ_SDK_ARCHIVE):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
.PHONY: downloads
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
# Vendor Go dependencies for the Docker tests
test/docker/vendor:
cd test/docker && dep ensure -vendor-only
.PHONY: check-prereqs
check-prereqs:
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
which buildah || (echo "Missing required program buildah" && exit 1)
which podman || (echo "Missing required program podman" && exit 1)
yum list | grep yum-utils || (echo "Missing required package yum-utils" && exit 1)
.PHONY: check-test-prereqs
check-prereqs:
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
which buildah || (echo "Missing required program buildah" && exit 1)
which docker || (echo "Missing required program docker" && exit 1)
.PHONY: test-advancedserver
test-advancedserver: check-test-prereqs test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
sudo buildah push $(MQ_IMAGE_ADVANCEDSERVER) docker-daemon:$(MQ_IMAGE_ADVANCEDSERVER)
docker tag docker.io/$(MQ_IMAGE_ADVANCEDSERVER) $(MQ_IMAGE_ADVANCEDSERVER)
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test $(TEST_OPTS_DOCKER)
.PHONY: test-devserver
test-devserver: check-test-prereqs test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
sudo buildah push $(MQ_IMAGE_DEVSERVER) docker-daemon:$(MQ_IMAGE_DEVSERVER)
docker tag docker.io/$(MQ_IMAGE_DEVSERVER) $(MQ_IMAGE_DEVSERVER)
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) go test -tags mqdev $(TEST_OPTS_DOCKER)
.PHONY: build-advancedserver
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
build-advancedserver: check-prereqs downloads/$(MQ_ARCHIVE) build-go-programs-ex
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_ADVANCEDSERVER)" "$(MQ_VERSION)" "$(MQDEV)"
.PHONY: build-devserver
build-devserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE_DEV)
build-devserver: MQDEV=TRUE
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
build-devserver: check-prereqs downloads/$(MQ_ARCHIVE_DEV) build-go-programs-ex
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER)"$(END)))
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE_DEV)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_VERSION)" "$(MQDEV)"
sudo mq-advanced-server-rhel/mqdev-buildah.sh "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_IMAGE_DEVSERVER)" "$(MQ_VERSION)"
.PHONY: build-mqgolang-sdk
build-mqgolang-sdk: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-mqgolang-sdk-ex
.PHONY: build-mqgolang-sdk-ex
build-mqgolang-sdk-ex:
$(info $(SPACER)$(shell printf $(TITLE)"Build mq-golang SDK"$(END)))
sudo mq-advanced-server-rhel/mq-golang-sdk-buildah.sh "$(MQ_SDK_ARCHIVE)" "$(MQ_IMAGE_GOLANG_SDK)"
.PHONY: build-go-programs
build-go-programs: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-go-programs-ex
.PHONY: build-go-programs-ex
build-go-programs-ex: build-mqgolang-sdk-ex
$(info $(SPACER)$(shell printf $(TITLE)"Build go programs"$(END)))
IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)"
.PHONY: build-devjmstest
build-devjmstest: check-test-prereqs
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
cd test/messaging && sudo ./buildah.sh $(DEV_JMS_IMAGE)
sudo buildah push $(DEV_JMS_IMAGE) docker-daemon:$(DEV_JMS_IMAGE)
docker tag docker.io/$(DEV_JMS_IMAGE) $(DEV_JMS_IMAGE)
include formatting.mk

View File

@@ -1,273 +0,0 @@
# © Copyright IBM Corporation 2017, 2018
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
# Conditional variables - you can override the values of these variables from
# the command line
###############################################################################
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
BASE_IMAGE ?= ubuntu:16.04
# MQ_VERSION is the fully qualified MQ version number to build
MQ_VERSION ?= 9.1.1.0
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
# Does not apply to MQ Advanced for Developers.
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
# for Developers can be installed
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
# Options to `go test` for the Docker tests
TEST_OPTS_DOCKER ?=
# MQ_IMAGE_ADVANCEDSERVER is the name and tag of the built MQ Advanced image
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# DOCKER is the Docker command to run
DOCKER ?= docker
# MQ_PACKAGES specifies the MQ packages (.deb or .rpm) to install. Defaults vary on base image.
MQ_PACKAGES ?=
###############################################################################
# Other variables
###############################################################################
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
ARCH = $(shell uname -m)
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
BUILD_SERVER_CONTAINER=build-server
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
# test run in parallel
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
# Docker image name to use for JMS tests
DEV_JMS_IMAGE=mq-dev-jms-test
# Variables for versioning
IMAGE_REVISION=$(shell git rev-parse HEAD)
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
ifneq (,$(findstring Microsoft,$(shell uname -r)))
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
else
DOWNLOADS_DIR=$(realpath ./downloads/)
endif
# Try to figure out which archive to use from the BASE_IMAGE
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
MQ_ARCHIVE_TYPE=UBUNTU
MQ_ARCHIVE_DEV_PLATFORM=ubuntu
else
MQ_ARCHIVE_TYPE=LINUX
MQ_ARCHIVE_DEV_PLATFORM=linux
endif
# Try to figure out which archive to use from the architecture
ifeq "$(ARCH)" "x86_64"
MQ_ARCHIVE_ARCH=X86-64
MQ_DEV_ARCH=x86-64
else ifeq "$(ARCH)" "ppc64le"
MQ_ARCHIVE_ARCH=LE_POWER
MQ_DEV_ARCH=ppcle
else ifeq "$(ARCH)" "s390x"
MQ_ARCHIVE_ARCH=SYSTEM_Z
MQ_DEV_ARCH=s390x
endif
# Archive names for IBM MQ Advanced for Developers
MQ_ARCHIVE_DEV_9.0.5.0=mqadv_dev905_$(MQ_ARCHIVE_DEV_PLATFORM)_x86-64.tar.gz
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev911_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
###############################################################################
# Build targets
###############################################################################
.PHONY: vars
vars:
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
@echo $(MQ_ARCHIVE_ARCH)
@echo $(MQ_ARCHIVE_TYPE)
@echo $(MQ_ARCHIVE)
.PHONY: default
default: build-devserver test
# Build all components (except incubating ones)
.PHONY: all
all: build-devserver build-advancedserver
.PHONY: test-all
test-all: build-devjmstest test-devserver test-advancedserver
.PHONY: devserver
devserver: build-devserver build-devjmstest test-devserver
# Build incubating components
.PHONY: incubating
incubating: build-explorer
downloads/$(MQ_ARCHIVE_DEV):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
downloads/$(MQ_SDK_ARCHIVE):
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
mkdir -p downloads
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
.PHONY: downloads
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
# Vendor Go dependencies for the Docker tests
test/docker/vendor:
cd test/docker && dep ensure -vendor-only
# Shortcut to just run the unit tests
.PHONY: test-unit
test-unit:
docker build --target builder --file Dockerfile-server .
.PHONY: test-advancedserver
test-advancedserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
.PHONY: build-devjmstest
build-devjmstest:
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
.PHONY: test-devserver
test-devserver: test/docker/vendor
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
coverage:
mkdir coverage
.PHONY: test-advancedserver-cover
test-advancedserver-cover: test/docker/vendor coverage
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) with code coverage on $(shell docker --version)"$(END)))
rm -f ./coverage/unit*.cov
# Run unit tests with coverage, for each package under 'internal'
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
echo 'mode: count' > ./coverage/unit.cov
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
rm -f ./test/docker/coverage/*.cov
rm -f ./coverage/docker.*
mkdir -p ./test/docker/coverage/
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
echo 'mode: count' > ./coverage/docker.cov
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
echo 'mode: count' > ./coverage/combined.cov
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
define docker-build-mq
# Create a temporary network to use for the build
$(DOCKER) network create build
# Start a web server to host the MQ downloadable (tar.gz) file
$(DOCKER) run \
--rm \
--name $(BUILD_SERVER_CONTAINER) \
--network build \
--network-alias build \
--volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro \
--detach \
nginx:alpine
# Build the new image
$(DOCKER) build \
--tag $1 \
--file $2 \
--network build \
--build-arg MQ_URL=http://build:80/$3 \
--build-arg BASE_IMAGE=$(BASE_IMAGE) \
--build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) \
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
--label IBM_PRODUCT_ID=$4 \
--label IBM_PRODUCT_NAME=$5 \
--label IBM_PRODUCT_VERSION=$6 \
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
endef
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
.PHONY: docker-version
docker-version:
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
.PHONY: build-advancedserver
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
build-advancedserver: downloads/$(MQ_ARCHIVE) docker-version build-golang-sdk-ex
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
$(call docker-build-mq,$(MQ_IMAGE_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
.PHONY: build-devserver
# Target-specific variable to add web server into devserver image
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
build-devserver: MQ_PACKAGES=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams ibmmq-web
else
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
endif
build-devserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE_DEV)
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version build-golang-sdk-ex
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER_BASE)"$(END)))
$(call docker-build-mq,$(MQ_IMAGE_DEVSERVER_BASE),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
$(DOCKER) build --tag $(MQ_IMAGE_DEVSERVER) --build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" --build-arg IMAGE_REVISION="$(IMAGE_REVISION)" --build-arg BASE_IMAGE=$(MQ_IMAGE_DEVSERVER_BASE) --build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) --file incubating/mqadvanced-server-dev/Dockerfile .
.PHONY: build-advancedserver-cover
build-advancedserver-cover: docker-version
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_IMAGE_ADVANCEDSERVER)-cover -f Dockerfile-server.cover .
.PHONY: build-explorer
build-explorer: downloads/$(MQ_ARCHIVE_DEV) docker-pull
$(call docker-build-mq,mq-explorer:latest-$(ARCH),incubating/mq-explorer/Dockerfile-mq-explorer,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
.PHONY: build-sdk
build-sdk: downloads/$(MQ_SDK_ARCHIVE) build-sdk-ex
.PHONY: build-sdk-ex
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
build-sdk-ex: MQ_PACKAGES=ibmmq-sdk ibmmq-samples build-essential
else
build-sdk-ex: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm
endif
build-sdk-ex: docker-version docker-pull
$(call docker-build-mq,$(MQ_IMAGE_SDK),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers SDK (Non-Warranted)",$(MQ_VERSION))
.PHONY: build-golang-sdk
build-golang-sdk: downloads/$(MQ_SDK_ARCHIVE) build-golang-sdk-ex
.PHONY: build-golang-sdk-ex
build-golang-sdk-ex: docker-version build-sdk-ex
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_SDK) -t $(MQ_IMAGE_GOLANG_SDK) -f incubating/mq-golang-sdk/Dockerfile .
# $(call docker-build-mq,$(MQ_IMAGE_GOLANG_SDK),incubating/mq-golang-sdk/Dockerfile,$(MQ_IMAGE_SDK),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers SDK (Non-Warranted)",$(MQ_VERSION))
.PHONY: docker-pull
docker-pull:
$(DOCKER) pull $(BASE_IMAGE)
include formatting.mk

12650
NOTICES.txt

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,6 @@
**Note**: The `master` branch may be in an *unstable or even broken state* during development. **Note**: The `master` branch may be in an *unstable or even broken state* during development.
To get a stable version, please use the correct [branch](https://github.com/ibm-messaging/mq-container/branches) for your MQ version, instead of the `master` branch. To get a stable version, please use the correct [branch](https://github.com/ibm-messaging/mq-container/branches) for your MQ version, instead of the `master` branch.
<img src="https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-mq-icon.svg?sanitize=true" width="100" alt="IBM MQ logo" />
## Overview ## Overview
Run [IBM® MQ](http://www-03.ibm.com/software/products/en/ibm-mq) in a container. Run [IBM® MQ](http://www-03.ibm.com/software/products/en/ibm-mq) in a container.
@@ -46,12 +44,11 @@ For issues relating specifically to the container image or Helm chart, please us
The Dockerfiles and associated code and scripts are licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). The Dockerfiles and associated code and scripts are licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
Licenses for the products installed within the images are as follows: Licenses for the products installed within the images are as follows:
- [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-AVCJ4S) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above. - [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-BMKG5H) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-AZYF4X) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above. - [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-BMJJBM) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
- License information for Ubuntu packages may be found in `/usr/share/doc/${package}/copyright`
Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine. Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine.
## Copyright ## Copyright
© Copyright IBM Corporation 2015, 2018 © Copyright IBM Corporation 2015, 2020

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017 © Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/ibm-messaging/mq-container/internal/name" "github.com/ibm-messaging/mq-container/pkg/name"
) )
func queueManagerHealthy() (bool, error) { func queueManagerHealthy() (bool, error) {
@@ -41,7 +41,7 @@ func queueManagerHealthy() (bool, error) {
return false, err return false, err
} }
fmt.Printf("%s", out) fmt.Printf("%s", out)
if !strings.Contains(string(out), "(RUNNING)") { if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
return false, nil return false, nil
} }
return true, nil return true, nil

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import (
"os" "os"
"github.com/ibm-messaging/mq-container/internal/ready" "github.com/ibm-messaging/mq-container/internal/ready"
"github.com/ibm-messaging/mq-container/pkg/name"
) )
func main() { func main() {
@@ -31,14 +32,25 @@ func main() {
if !r || err != nil { if !r || err != nil {
os.Exit(1) os.Exit(1)
} }
// Check if the queue manager has a running listener name, err := name.GetQueueManagerName()
conn, err := net.Dial("tcp", "127.0.0.1:1414")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
err = conn.Close()
if err != nil { // Check if the queue manager has a running listener
fmt.Println(err) if standby, _ := ready.IsRunningAsStandbyQM(name); !standby {
conn, err := net.Dial("tcp", "127.0.0.1:1414")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = conn.Close()
if err != nil {
fmt.Println(err)
}
} else {
fmt.Printf("Detected queue manager running in standby mode")
os.Exit(10)
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -22,16 +22,17 @@ import (
"os/exec" "os/exec"
"syscall" "syscall"
"github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/internal/htpasswd"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
"github.com/ibm-messaging/mq-container/internal/name" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-container/pkg/name"
) )
var log *logger.Logger var log *logger.Logger
func setPassword(user string, password string) error { func setPassword(user string, password string) error {
// #nosec G204 // #nosec G204
cmd := exec.Command("chpasswd") cmd := exec.Command("sudo", "chpasswd")
stdin, err := cmd.StdinPipe() stdin, err := cmd.StdinPipe()
if err != nil { if err != nil {
return err return err
@@ -41,9 +42,10 @@ func setPassword(user string, password string) error {
if err != nil { if err != nil {
log.Errorf("Error closing password stdin: %v", err) log.Errorf("Error closing password stdin: %v", err)
} }
_, _, err = command.RunCmd(cmd) out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return err // Include the command output in the error
return fmt.Errorf("%v: %v", err.Error(), out)
} }
log.Printf("Set password for \"%v\" user", user) log.Printf("Set password for \"%v\" user", user)
return nil return nil
@@ -87,22 +89,17 @@ func configureLogger() error {
return nil return nil
} }
func configureWeb(qmName string) error {
out := "/etc/mqm/web/installations/Installation1/angular.persistence/admin.json"
return processTemplateFile("/etc/mqm/admin.json.tpl", out, map[string]string{"QueueManagerName": qmName})
}
func logTerminationf(format string, args ...interface{}) { func logTerminationf(format string, args ...interface{}) {
logTermination(fmt.Sprintf(format, args)) logTermination(fmt.Sprintf(format, args...))
} }
// TODO: Duplicated code // TODO: Duplicated code
func logTermination(args ...interface{}) { func logTermination(args ...interface{}) {
msg := fmt.Sprint(args) msg := fmt.Sprint(args...)
// Write the message to the termination log. This is the default place // Write the message to the termination log. This is not the default place
// that Kubernetes will look for termination information. // that Kubernetes will look for termination information.
log.Debugf("Writing termination message: %v", msg) log.Debugf("Writing termination message: %v", msg)
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660) err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
} }
@@ -115,17 +112,31 @@ func doMain() error {
logTermination(err) logTermination(err)
return err return err
} }
err = containerruntimelogger.LogContainerDetails(log)
if err != nil {
logTermination(err)
return err
}
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD") adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
if set { if !set {
err = setPassword("admin", adminPassword) adminPassword = "passw0rd"
err = os.Setenv("MQ_ADMIN_PASSWORD", adminPassword)
if err != nil { if err != nil {
logTerminationf("Error setting admin password: %v", err) logTerminationf("Error setting admin password variable: %v", err)
return err return err
} }
} }
err = htpasswd.SetPassword("admin", adminPassword, false)
if err != nil {
logTerminationf("Error setting admin password: %v", err)
return err
}
appPassword, set := os.LookupEnv("MQ_APP_PASSWORD") appPassword, set := os.LookupEnv("MQ_APP_PASSWORD")
if set { if set {
err = setPassword("app", appPassword) err = htpasswd.SetPassword("app", appPassword, false)
if err != nil { if err != nil {
logTerminationf("Error setting app password: %v", err) logTerminationf("Error setting app password: %v", err)
return err return err
@@ -138,26 +149,6 @@ func doMain() error {
return err return err
} }
name, err := name.GetQueueManagerName()
if err != nil {
logTerminationf("Error getting queue manager name: %v", err)
return err
}
ks, set := os.LookupEnv("MQ_TLS_KEYSTORE")
if set {
err = configureTLS(name, ks, os.Getenv("MQ_TLS_PASSPHRASE"))
if err != nil {
logTerminationf("Error configuring TLS: %v", err)
return err
}
}
err = configureWeb(name)
if err != nil {
logTermination("Error configuring admin.json")
return err
}
return nil return nil
} }
@@ -170,7 +161,7 @@ func main() {
} else { } else {
// Replace this process with runmqserver // Replace this process with runmqserver
// #nosec G204 // #nosec G204
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver"}, os.Environ()) err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-nologruntime", "-dev"}, os.Environ())
if err != nil { if err != nil {
log.Errorf("Error replacing this process with runmqserver: %v", err) log.Errorf("Error replacing this process with runmqserver: %v", err)
} }

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@ package main
import ( import (
"os" "os"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
) )
func updateMQSC(appPasswordRequired bool) error { func updateMQSC(appPasswordRequired bool) error {
@@ -30,7 +32,7 @@ func updateMQSC(appPasswordRequired bool) error {
if os.Getenv("MQ_DEV") == "true" { if os.Getenv("MQ_DEV") == "true" {
const mqscTemplate string = mqsc + ".tpl" const mqscTemplate string = mqsc + ".tpl"
// Re-configure channel if app password not set // Re-configure channel if app password not set
err := processTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient}) err := mqtemplate.ProcessTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient}, log)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,164 +0,0 @@
/*
© Copyright IBM Corporation 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/ibm-messaging/mq-container/internal/command"
)
func configureWebTLS(cms *KeyStore) error {
dir := "/run/runmqdevserver/tls"
ks := NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
ts := NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)
log.Debug("Creating key store")
err := ks.Create()
if err != nil {
return err
}
log.Debug("Creating trust store")
err = ts.Create()
if err != nil {
return err
}
log.Debug("Importing keys")
err = ks.Import(cms.Filename, cms.Password)
if err != nil {
return err
}
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
tlsConfig := filepath.Join(webConfigDir, "tls.xml")
newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
err = os.Remove(tlsConfig)
if err != nil {
return err
}
// we symlink here to prevent issues on restart
err = os.Symlink(newTLSConfig, tlsConfig)
if err != nil {
return err
}
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(tlsConfig, mqmUID, mqmGID)
if err != nil {
log.Error(err)
return err
}
return nil
}
func configureTLS(qmName string, inputFile string, passPhrase string) error {
log.Debug("Configuring TLS")
_, err := os.Stat(inputFile)
if err != nil {
return err
}
// TODO: Use a persisted file (on the volume) instead?
dir := "/run/runmqdevserver/tls"
keyFile := filepath.Join(dir, "key.kdb")
_, err = os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
// #nosec G301
err = os.MkdirAll(dir, 0770)
if err != nil {
return err
}
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(dir, mqmUID, mqmGID)
if err != nil {
log.Error(err)
return err
}
} else {
return err
}
}
cms := NewCMSKeyStore(keyFile, passPhrase)
err = cms.Create()
if err != nil {
return err
}
err = cms.CreateStash()
if err != nil {
return err
}
err = cms.Import(inputFile, passPhrase)
if err != nil {
return err
}
labels, err := cms.GetCertificateLabels()
if err != nil {
return err
}
if len(labels) == 0 {
return fmt.Errorf("unable to find certificate label")
}
log.Debugf("Renaming certificate from %v", labels[0])
const newLabel string = "devcert"
err = cms.RenameCertificate(labels[0], newLabel)
if err != nil {
return err
}
var sslCipherSpec string
if os.Getenv("MQ_DEV") == "true" {
sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
} else {
sslCipherSpec = "' '"
}
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
err = processTemplateFile(mqscTemplate, mqsc, map[string]string{
"SSLKeyR": filepath.Join(dir, "key"),
"CertificateLabel": newLabel,
"SSLCipherSpec": sslCipherSpec,
})
if err != nil {
return err
}
err = configureWebTLS(cms)
if err != nil {
return err
}
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -17,16 +17,10 @@ package main
import ( import (
"os" "os"
"path/filepath"
"runtime"
"syscall"
"github.com/ibm-messaging/mq-container/internal/command"
) )
func createVolume(path string) error { func createVolume(dataPath string) error {
dataPath := filepath.Join(path, "data") _, err := os.Stat(dataPath)
fi, err := os.Stat(dataPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// #nosec G301 // #nosec G301
@@ -38,25 +32,5 @@ func createVolume(path string) error {
return err return err
} }
} }
fi, err = os.Stat(dataPath)
if err != nil {
return err
}
sys := fi.Sys()
if sys != nil && runtime.GOOS == "linux" {
stat := sys.(*syscall.Stat_t)
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
return err
}
log.Debugf("mqm user is %v (%v)", mqmUID, mqmGID)
if int(stat.Uid) != mqmUID || int(stat.Gid) != mqmGID {
err = os.Chown(dataPath, mqmUID, mqmGID)
if err != nil {
log.Printf("Error: Unable to change ownership of %v", dataPath)
return err
}
}
}
return nil return nil
} }

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,11 +23,13 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"sort"
"strings"
"sync" "sync"
"github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/internal/command"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-container/internal/mqini" "github.com/ibm-messaging/mq-container/pkg/mqini"
) )
// var debug = false // var debug = false
@@ -36,15 +38,15 @@ var log *logger.Logger
var collectDiagOnFail = false var collectDiagOnFail = false
func logTerminationf(format string, args ...interface{}) { func logTerminationf(format string, args ...interface{}) {
logTermination(fmt.Sprintf(format, args)) logTermination(fmt.Sprintf(format, args...))
} }
func logTermination(args ...interface{}) { func logTermination(args ...interface{}) {
msg := fmt.Sprint(args) msg := fmt.Sprint(args...)
// Write the message to the termination log. This is the default place // Write the message to the termination log. This is not the default place
// that Kubernetes will look for termination information. // that Kubernetes will look for termination information.
log.Debugf("Writing termination message: %v", msg) log.Debugf("Writing termination message: %v", msg)
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660) err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
} }
@@ -59,14 +61,31 @@ func getLogFormat() string {
return os.Getenv("LOG_FORMAT") return os.Getenv("LOG_FORMAT")
} }
func formatSimple(datetime string, message string) string { // formatBasic formats a log message parsed from JSON, as "basic" text
return fmt.Sprintf("%v %v\n", datetime, message) func formatBasic(obj map[string]interface{}) string {
// Emulate the MQ "MessageDetail=Extended" option, by appending inserts to the message
// This is important for certain messages, where key details are only available in the extended message content
inserts := make([]string, 0)
for k, v := range obj {
if strings.HasPrefix(k, "ibm_commentInsert") {
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_comment", "Comment", 1), obj[k]))
} else if strings.HasPrefix(k, "ibm_arithInsert") {
if v.(float64) != 0 {
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_arith", "Arith", 1), obj[k]))
}
}
}
sort.Strings(inserts)
if len(inserts) > 0 {
return fmt.Sprintf("%s %s [%v]\n", obj["ibm_datetime"], obj["message"], strings.Join(inserts, ", "))
}
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
} }
// mirrorSystemErrorLogs starts a goroutine to mirror the contents of the MQ system error logs // mirrorSystemErrorLogs starts a goroutine to mirror the contents of the MQ system error logs
func mirrorSystemErrorLogs(ctx context.Context, wg *sync.WaitGroup, mf mirrorFunc) (chan error, error) { func mirrorSystemErrorLogs(ctx context.Context, wg *sync.WaitGroup, mf mirrorFunc) (chan error, error) {
// Always use the JSON log as the source // Always use the JSON log as the source
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf) return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf, false)
} }
// mirrorQueueManagerErrorLogs starts a goroutine to mirror the contents of the MQ queue manager error logs // mirrorQueueManagerErrorLogs starts a goroutine to mirror the contents of the MQ queue manager error logs
@@ -78,7 +97,7 @@ func mirrorQueueManagerErrorLogs(ctx context.Context, wg *sync.WaitGroup, name s
return nil, err return nil, err
} }
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json") f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json")
return mirrorLog(ctx, wg, f, fromStart, mf) return mirrorLog(ctx, wg, f, fromStart, mf, true)
} }
func getDebug() bool { func getDebug() bool {
@@ -99,21 +118,36 @@ func configureLogger(name string) (mirrorFunc, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return log.LogDirect, nil return func(msg string, isQMLog bool) bool {
obj, err := processLogMessage(msg)
if err == nil && isQMLog && filterQMLogMessage(obj) {
return false
}
if err != nil {
log.Printf("Failed to unmarshall JSON - %v", msg)
} else {
fmt.Println(msg)
}
return true
}, nil
case "basic": case "basic":
log, err = logger.NewLogger(os.Stderr, d, false, name) log, err = logger.NewLogger(os.Stderr, d, false, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return func(msg string) { return func(msg string, isQMLog bool) bool {
// Parse the JSON message, and print a simplified version // Parse the JSON message, and print a simplified version
var obj map[string]interface{} obj, err := processLogMessage(msg)
err := json.Unmarshal([]byte(msg), &obj) if err == nil && isQMLog && filterQMLogMessage(obj) {
if err != nil { return false
fmt.Printf("Failed to Unmarshall JSON - %v", err)
} else {
fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string)))
} }
if err != nil {
log.Printf("Failed to unmarshall JSON - %v", err)
} else {
fmt.Printf(formatBasic(obj))
// fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string)))
}
return true
}, nil }, nil
default: default:
log, err = logger.NewLogger(os.Stdout, d, false, name) log, err = logger.NewLogger(os.Stdout, d, false, name)
@@ -124,33 +158,58 @@ func configureLogger(name string) (mirrorFunc, error) {
} }
} }
func logDiagnostics() { func processLogMessage(msg string) (map[string]interface{}, error) {
log.Debug("--- Start Diagnostics ---") var obj map[string]interface{}
err := json.Unmarshal([]byte(msg), &obj)
// show the directory ownership/permissions return obj, err
// #nosec G104 }
out, _, _ := command.Run("ls", "-l", "/mnt/")
log.Debugf("/mnt/:\n%s", out) func filterQMLogMessage(obj map[string]interface{}) bool {
// #nosec G104 hostname, err := os.Hostname()
out, _, _ = command.Run("ls", "-l", "/mnt/mqm") if os.Getenv("MQ_MULTI_INSTANCE") == "true" && err == nil && !strings.Contains(obj["host"].(string), hostname) {
log.Debugf("/mnt/mqm:\n%s", out) return true
// #nosec G104 }
out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data") return false
log.Debugf("/mnt/mqm/data:\n%s", out) }
// #nosec G104
out, _, _ = command.Run("ls", "-l", "/var/mqm") func logDiagnostics() {
log.Debugf("/var/mqm:\n%s", out) if getDebug() {
// #nosec G104 log.Debug("--- Start Diagnostics ---")
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
log.Debugf("/var/mqm/errors:\n%s", out) // show the directory ownership/permissions
// #nosec G104
// Print out summary of any FDCs out, _, _ := command.Run("ls", "-l", "/mnt/")
// #nosec G204 log.Debugf("/mnt/:\n%s", out)
cmd := exec.Command("/opt/mqm/bin/ffstsummary") // #nosec G104
cmd.Dir = "/var/mqm/errors" out, _, _ = command.Run("ls", "-l", "/mnt/mqm")
// #nosec G104 log.Debugf("/mnt/mqm:\n%s", out)
outB, _ := cmd.CombinedOutput() // #nosec G104
log.Debugf("ffstsummary:\n%s", string(outB)) out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data")
log.Debugf("/mnt/mqm/data:\n%s", out)
log.Debug("--- End Diagnostics ---") // #nosec G104
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-log/log")
log.Debugf("/mnt/mqm-log/log:\n%s", out)
// #nosec G104
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-data/qmgrs")
log.Debugf("/mnt/mqm-data/qmgrs:\n%s", out)
// #nosec G104
out, _, _ = command.Run("ls", "-l", "/var/mqm")
log.Debugf("/var/mqm:\n%s", out)
// #nosec G104
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
log.Debugf("/var/mqm/errors:\n%s", out)
// #nosec G104
out, _, _ = command.Run("ls", "-l", "/etc/mqm")
log.Debugf("/etc/mqm:\n%s", out)
// Print out summary of any FDCs
// #nosec G204
cmd := exec.Command("/opt/mqm/bin/ffstsummary")
cmd.Dir = "/var/mqm/errors"
// #nosec G104
outB, _ := cmd.CombinedOutput()
log.Debugf("ffstsummary:\n%s", string(outB))
log.Debug("--- End Diagnostics ---")
}
} }

View File

@@ -0,0 +1,55 @@
/*
© Copyright IBM Corporation 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"encoding/json"
"fmt"
"strings"
"testing"
)
var formatBasicTests = []struct {
in []byte
outContains string
}{
{
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\"}"),
"Hello",
},
{
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_commentInsert1\":\"foo\"}"),
"CommentInsert1(foo)",
},
{
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_arithInsert1\":1}"),
"ArithInsert1(1)",
},
}
func TestFormatBasic(t *testing.T) {
for i, table := range formatBasicTests {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
var inObj map[string]interface{}
json.Unmarshal(table.in, &inObj)
t.Logf("Unmarshalled: %+v", inObj)
out := formatBasic(inObj)
if !strings.Contains(out, table.outContains) {
t.Errorf("formatBasic() with input=%v - expected output to contain %v, got %v", string(table.in), table.outContains, out)
}
})
}
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,21 +20,48 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"flag"
"os" "os"
"sync" "sync"
"github.com/ibm-messaging/mq-container/internal/metrics" "github.com/ibm-messaging/mq-container/internal/metrics"
"github.com/ibm-messaging/mq-container/internal/name"
"github.com/ibm-messaging/mq-container/internal/ready" "github.com/ibm-messaging/mq-container/internal/ready"
"github.com/ibm-messaging/mq-container/internal/tls"
"github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
"github.com/ibm-messaging/mq-container/pkg/name"
) )
func doMain() error { func doMain() error {
var initFlag = flag.Bool("i", false, "initialize volume only, then exit")
var infoFlag = flag.Bool("info", false, "Display debug info, then exit")
var noLogRuntimeFlag = flag.Bool("nologruntime", false, "used when running this program from another program, to control log output")
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control how TLS is configured")
flag.Parse()
name, nameErr := name.GetQueueManagerName() name, nameErr := name.GetQueueManagerName()
mf, err := configureLogger(name) mf, err := configureLogger(name)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
// Check whether they only want debug info
if *infoFlag {
logVersionInfo()
err = containerruntimelogger.LogContainerDetails(log)
if err != nil {
log.Printf("Error displaying container details: %v", err)
}
return nil
}
err = verifySingleProcess()
if err != nil {
// We don't do the normal termination here as it would create a termination file.
log.Error(err)
return err
}
if nameErr != nil { if nameErr != nil {
logTermination(err) logTermination(err)
return err return err
@@ -61,43 +88,85 @@ func doMain() error {
// Enable diagnostic collecting on failure // Enable diagnostic collecting on failure
collectDiagOnFail = true collectDiagOnFail = true
err = verifyCurrentUser() if *noLogRuntimeFlag == false {
err = containerruntimelogger.LogContainerDetails(log)
if err != nil {
logTermination(err)
return err
}
}
err = createVolume("/mnt/mqm/data")
if err != nil {
logTermination(err)
return err
}
err = createVolume("/mnt/mqm-log/log")
if err != nil {
logTermination(err)
return err
}
err = createVolume("/mnt/mqm-data/qmgrs")
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
err = logConfig() enableTraceCrtmqdir := os.Getenv("MQ_ENABLE_TRACE_CRTMQDIR")
if err != nil { if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
logTermination(err) err = startMQTrace()
return err if err != nil {
logTermination(err)
return err
}
} }
err = createVolume("/mnt/mqm")
if err != nil {
logTermination(err)
return err
}
err = createDirStructure() err = createDirStructure()
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
err = endMQTrace()
if err != nil {
logTermination(err)
return err
}
}
// If init flag is set, exit now
if *initFlag {
return nil
}
// Print out versioning information // Print out versioning information
logVersionInfo() logVersionInfo()
err = postInit(name) keyLabel, cmsKeystore, p12Truststore, err := tls.ConfigureTLSKeystores()
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
newQM, err := createQueueManager(name) err = tls.ConfigureTLS(keyLabel, cmsKeystore, *devFlag, log)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
err = postInit(name, keyLabel, p12Truststore)
if err != nil {
logTermination(err)
return err
}
newQM, err := createQueueManager(name, *devFlag)
if err != nil {
logTermination(err)
return err
}
var wg sync.WaitGroup var wg sync.WaitGroup
defer func() { defer func() {
log.Debug("Waiting for log mirroring to complete") log.Debug("Waiting for log mirroring to complete")
@@ -124,15 +193,38 @@ func doMain() error {
logTermination(err) logTermination(err)
return err return err
} }
err = startQueueManager()
enableTraceStrmqm := os.Getenv("MQ_ENABLE_TRACE_STRMQM")
if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
err = startMQTrace()
if err != nil {
logTermination(err)
return err
}
}
// This is a developer image only change
// This workaround should be removed and handled via <crtmqm -ii>, when inimerge is ready to handle stanza ordering
if *devFlag {
err = updateQMini(name)
if err != nil {
logTermination(err)
return err
}
}
err = startQueueManager(name)
if err != nil { if err != nil {
logTermination(err) logTermination(err)
return err return err
} }
err = configureQueueManager()
if err != nil { if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
logTermination(err) err = endMQTrace()
return err if err != nil {
logTermination(err)
return err
}
} }
enableMetrics := os.Getenv("MQ_ENABLE_METRICS") enableMetrics := os.Getenv("MQ_ENABLE_METRICS")

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
) )
var test *bool var test *bool

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -49,16 +49,17 @@ func waitForFile(ctx context.Context, path string) (os.FileInfo, error) {
} }
} }
type mirrorFunc func(msg string) type mirrorFunc func(msg string, isQMLog bool) bool
// mirrorAvailableMessages prints lines from the file, until no more are available // mirrorAvailableMessages prints lines from the file, until no more are available
func mirrorAvailableMessages(f *os.File, mf mirrorFunc) { func mirrorAvailableMessages(f *os.File, mf mirrorFunc, isQMLog bool) {
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
count := 0 count := 0
for scanner.Scan() { for scanner.Scan() {
t := scanner.Text() t := scanner.Text()
mf(t) if mf(t, isQMLog) {
count++ count++
}
} }
if count > 0 { if count > 0 {
log.Debugf("Mirrored %v log entries from %v", count, f.Name()) log.Debugf("Mirrored %v log entries from %v", count, f.Name())
@@ -73,7 +74,7 @@ func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
// mirrorLog tails the specified file, and logs each line to stdout. // mirrorLog tails the specified file, and logs each line to stdout.
// This is useful for usability, as the container console log can show // This is useful for usability, as the container console log can show
// messages from the MQ error logs. // messages from the MQ error logs.
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc) (chan error, error) { func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc, isQMLog bool) (chan error, error) {
errorChannel := make(chan error, 1) errorChannel := make(chan error, 1)
var offset int64 = -1 var offset int64 = -1
var f *os.File var f *os.File
@@ -147,7 +148,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
closing := false closing := false
for { for {
// If there's already data there, mirror it now. // If there's already data there, mirror it now.
mirrorAvailableMessages(f, mf) mirrorAvailableMessages(f, mf, isQMLog)
// Wait for the new log file (after rotation) // Wait for the new log file (after rotation)
newFI, err := waitForFile(ctx, path) newFI, err := waitForFile(ctx, path)
if err != nil { if err != nil {
@@ -161,7 +162,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
// log rotation happens before we can open the new file, then we // log rotation happens before we can open the new file, then we
// could skip all those messages. This could happen with a very small // could skip all those messages. This could happen with a very small
// MQ error log size. // MQ error log size.
mirrorAvailableMessages(f, mf) mirrorAvailableMessages(f, mf, isQMLog)
err = f.Close() err = f.Close()
if err != nil { if err != nil {
log.Errorf("Unable to close mirror file handle: %v", err) log.Errorf("Unable to close mirror file handle: %v", err)
@@ -176,7 +177,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
} }
fi = newFI fi = newFI
// Don't seek this time, because we know it's a new file // Don't seek this time, because we know it's a new file
mirrorAvailableMessages(f, mf) mirrorAvailableMessages(f, mf, isQMLog)
} }
select { select {
case <-ctx.Done(): case <-ctx.Done():

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -41,9 +41,10 @@ func TestMirrorLogWithoutRotation(t *testing.T) {
count := 0 count := 0
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup var wg sync.WaitGroup
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) { _, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
count++ count++
}) return true
}, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -82,9 +83,10 @@ func TestMirrorLogWithRotation(t *testing.T) {
count := 0 count := 0
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup var wg sync.WaitGroup
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) { _, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
count++ count++
}) return true
}, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -135,9 +137,10 @@ func testMirrorLogExistingFile(t *testing.T, newQM bool) int {
count := 0 count := 0
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup var wg sync.WaitGroup
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string) { _, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string, isQMLog bool) bool {
count++ count++
}) return true
}, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -179,8 +182,9 @@ func TestMirrorLogCancelWhileWaiting(t *testing.T) {
cancel() cancel()
wg.Wait() wg.Wait()
}() }()
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string) { _, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string, isQMLog bool) bool {
}) return true
}, false)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

View File

@@ -1,157 +0,0 @@
/*
© Copyright IBM Corporation 2017, 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"io/ioutil"
"runtime"
"strings"
"github.com/genuinetools/amicontained/container"
)
func logContainerRuntime() {
r, err := container.DetectRuntime()
if err != nil {
log.Printf("Failed to get container runtime: %v", err)
return
}
log.Printf("Container runtime: %v", r)
}
func logBaseImage() {
buf, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
log.Printf("Failed to read /etc/os-release: %v", err)
return
}
lines := strings.Split(string(buf), "\n")
for _, l := range lines {
if strings.HasPrefix(l, "PRETTY_NAME=") {
words := strings.Split(l, "\"")
if len(words) >= 2 {
log.Printf("Base image: %v", words[1])
return
}
}
}
}
// logCapabilities logs the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
func logCapabilities() {
caps, err := container.Capabilities()
if err != nil {
log.Printf("Failed to get container capabilities: %v", err)
return
}
for k, v := range caps {
if len(v) > 0 {
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
}
}
}
// logSeccomp logs the seccomp enforcing mode, which affects which kernel calls can be made
func logSeccomp() {
s, err := container.SeccompEnforcingMode()
if err != nil {
log.Printf("Failed to get container SeccompEnforcingMode: %v", err)
return
}
log.Printf("seccomp enforcing mode: %v", s)
}
// logSecurityAttributes logs the security attributes of the current process.
// The security attributes indicate whether AppArmor or SELinux are being used,
// and what the level of confinement is.
func logSecurityAttributes() {
a, err := readProc("/proc/self/attr/current")
// On some systems, if AppArmor or SELinux are not installed, you get an
// error when you try and read `/proc/self/attr/current`, even though the
// file exists.
if err != nil || a == "" {
a = "none"
}
log.Printf("Process security attributes: %v", a)
}
func readProc(filename string) (value string, err error) {
// #nosec G304
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(buf)), nil
}
func readMounts() error {
all, err := readProc("/proc/mounts")
if err != nil {
log.Print("Error: Couldn't read /proc/mounts")
return err
}
lines := strings.Split(all, "\n")
detected := false
for i := range lines {
parts := strings.Split(lines[i], " ")
//dev := parts[0]
mountPoint := parts[1]
fsType := parts[2]
if strings.Contains(mountPoint, "/mnt/mqm") {
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
detected = true
}
}
if !detected {
log.Print("No volume detected. Persistent messages may be lost")
} else {
return checkFS("/mnt/mqm")
}
return nil
}
func logConfig() error {
log.Printf("CPU architecture: %v", runtime.GOARCH)
if runtime.GOOS == "linux" {
var err error
osr, err := readProc("/proc/sys/kernel/osrelease")
if err != nil {
log.Print(err)
} else {
log.Printf("Linux kernel version: %v", osr)
}
logContainerRuntime()
logBaseImage()
fileMax, err := readProc("/proc/sys/fs/file-max")
if err != nil {
log.Print(err)
} else {
log.Printf("Maximum file handles: %v", fileMax)
}
logUser()
logCapabilities()
logSeccomp()
logSecurityAttributes()
err = readMounts()
if err != nil {
return err
}
} else {
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
}
return nil
}

View File

@@ -1,7 +1,5 @@
// +build mqdev
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,23 +17,31 @@ package main
import ( import (
"os" "os"
"github.com/ibm-messaging/mq-container/internal/tls"
) )
// postInit is run after /var/mqm is set up // postInit is run after /var/mqm is set up
// This version of postInit is only included as part of the MQ Advanced for Developers build func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
func postInit(name string) error { enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE") if enableWebServer == "true" || enableWebServer == "1" {
if disable != "true" && disable != "1" { // Configure the web server (if enabled)
// Configure the web server (if installed) webKeystore, err := configureWebServer(keyLabel, p12Truststore)
err := configureWebServer()
if err != nil { if err != nil {
return err return err
} }
// If trust-store is empty, set reference to point to the keystore
webTruststoreRef := "MQWebTrustStore"
if len(p12Truststore.TrustedCerts) == 0 {
webTruststoreRef = "MQWebKeyStore"
}
// Start the web server, in the background (if installed) // Start the web server, in the background (if installed)
// WARNING: No error handling or health checking available for the web server, // WARNING: No error handling or health checking available for the web server
// which is why it's limited to use with MQ Advanced for Developers only
go func() { go func() {
startWebServer() err = startWebServer(webKeystore, p12Truststore.Password, webTruststoreRef)
if err != nil {
log.Printf("Error starting web server: %v", err)
}
}() }()
} }
return nil return nil

View File

@@ -0,0 +1,64 @@
/*
© Copyright IBM Corporation 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
// Verifies that we are the main or only instance of this program
func verifySingleProcess() error {
programName, err := determineExecutable()
if err != nil {
return fmt.Errorf("Failed to determine name of this program - %v", err)
}
// Verify that there is only one runmqserver
_, err = verifyOnlyOne(programName)
if err != nil {
return fmt.Errorf("You cannot run more than one instance of this program")
}
return nil
}
// Verifies that there is only one instance running of the given program name.
func verifyOnlyOne(programName string) (int, error) {
// #nosec G104
out, _, _ := command.Run("ps", "-e", "--format", "cmd")
//if this goes wrong then assume we are the only one
numOfProg := strings.Count(out, programName)
if numOfProg != 1 {
return numOfProg, fmt.Errorf("Expected there to be only 1 instance of %s but found %d", programName, numOfProg)
}
return numOfProg, nil
}
// Determines the name of the currently running executable.
func determineExecutable() (string, error) {
file, err := os.Executable()
if err != nil {
return "", err
}
_, exec := filepath.Split(file)
return exec, nil
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,42 +16,81 @@ limitations under the License.
package main package main
import ( import (
"io" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"strings" "strings"
"github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/internal/command"
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
"github.com/ibm-messaging/mq-container/internal/mqscredact"
"github.com/ibm-messaging/mq-container/internal/ready"
) )
// createDirStructure creates the default MQ directory structure under /var/mqm // createDirStructure creates the default MQ directory structure under /var/mqm
func createDirStructure() error { func createDirStructure() error {
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s") // log file diagnostics before and after crtmqdir if DEBUG=true
logDiagnostics()
out, rc, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-a")
if err != nil { if err != nil {
log.Printf("Error creating directory structure: %v\n", string(out)) if rc == 10 {
return err log.Printf("Warning creating directory structure: %v\n", string(out))
} else {
log.Printf("Error creating directory structure: %v\n", string(out))
return err
}
} }
log.Println("Created directory structure under /var/mqm") log.Println("Created directory structure under /var/mqm")
logDiagnostics()
return nil return nil
} }
// createQueueManager creates a queue manager, if it doesn't already exist. // createQueueManager creates a queue manager, if it doesn't already exist.
// It returns true if one was created, or false if one already existed // It returns true if one was created (or a standby was created), or false if one already existed
func createQueueManager(name string) (bool, error) { func createQueueManager(name string, devMode bool) (bool, error) {
log.Printf("Creating queue manager %v", name) log.Printf("Creating queue manager %v", name)
out, rc, err := command.Run("crtmqm", "-q", "-p", "1414", name)
// Run 'dspmqinf' to check if 'mqs.ini' configuration file exists
// If command succeeds, the queue manager (or standby queue manager) has already been created
_, _, err := command.Run("dspmqinf", name)
if err == nil {
log.Printf("Detected existing queue manager %v", name)
return false, nil
}
mounts, err := containerruntime.GetMounts()
if err != nil { if err != nil {
// 8=Queue manager exists, which is fine log.Printf("Error getting mounts for queue manager")
if rc == 8 {
log.Printf("Detected existing queue manager %v", name)
return false, nil
}
log.Printf("crtmqm returned %v", rc)
log.Println(string(out))
return false, err return false, err
} }
// Check if 'qm.ini' configuration file exists for the queue manager
// TODO : handle possible race condition - use a file lock?
dataDir := getQueueManagerDataDir(mounts, name)
_, err = os.Stat(filepath.Join(dataDir, "qm.ini"))
if err != nil {
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
args := getCreateQueueManagerArgs(mounts, name, devMode)
out, rc, err := command.Run("crtmqm", args...)
if err != nil {
log.Printf("Error %v creating queue manager: %v", rc, string(out))
return false, err
}
} else {
// If 'qm.ini' is found - run 'addmqinf' to create a standby queue manager with existing configuration
args := getCreateStandbyQueueManagerArgs(name)
out, rc, err := command.Run("addmqinf", args...)
if err != nil {
log.Printf("Error %v creating standby queue manager: %v", rc, string(out))
return false, err
}
log.Println("Created standby queue manager")
return true, nil
}
log.Println("Created queue manager")
return true, nil return true, nil
} }
@@ -68,10 +107,15 @@ func updateCommandLevel() error {
return nil return nil
} }
func startQueueManager() error { func startQueueManager(name string) error {
log.Println("Starting queue manager") log.Println("Starting queue manager")
out, rc, err := command.Run("strmqm") out, rc, err := command.Run("strmqm", "-x", name)
if err != nil { if err != nil {
// 30=standby queue manager started, which is fine
if rc == 30 {
log.Printf("Started standby queue manager")
return nil
}
log.Printf("Error %v starting queue manager: %v", rc, string(out)) log.Printf("Error %v starting queue manager: %v", rc, string(out))
return err return err
} }
@@ -79,62 +123,150 @@ func startQueueManager() error {
return nil return nil
} }
func configureQueueManager() error { func stopQueueManager(name string) error {
const configDir string = "/etc/mqm" log.Println("Stopping queue manager")
files, err := ioutil.ReadDir(configDir) qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
isStandby, err := ready.IsRunningAsStandbyQM(name)
if err != nil { if err != nil {
log.Println(err) log.Printf("Error getting status for queue manager %v: %v", name, err.Error())
return err return err
} }
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
if isStandby {
args = []string{"-x", name}
} else {
args = []string{"-s", "-w", "-r", "-tp", qmGracePeriod, name}
}
}
out, rc, err := command.Run("endmqm", args...)
if err != nil {
log.Printf("Error %v stopping queue manager: %v", rc, string(out))
return err
}
if isStandby {
log.Printf("Stopped standby queue manager")
} else {
log.Println("Stopped queue manager")
}
return nil
}
for _, file := range files { func startMQTrace() error {
if strings.HasSuffix(file.Name(), ".mqsc") { log.Println("Starting MQ trace")
abs := filepath.Join(configDir, file.Name()) out, rc, err := command.Run("strmqtrc")
// #nosec G204 if err != nil {
cmd := exec.Command("runmqsc") log.Printf("Error %v starting trace: %v", rc, string(out))
stdin, err := cmd.StdinPipe() return err
if err != nil { }
log.Println(err) log.Println("Started MQ trace")
return err return nil
} }
// Open the MQSC file for reading
// #nosec G304 func endMQTrace() error {
f, err := os.Open(abs) log.Println("Ending MQ Trace")
if err != nil { out, rc, err := command.Run("endmqtrc")
log.Printf("Error opening %v: %v", abs, err) if err != nil {
} log.Printf("Error %v ending trace: %v", rc, string(out))
// Copy the contents to stdin of the runmqsc process return err
_, err = io.Copy(stdin, f) }
if err != nil { log.Println("Ended MQ trace")
log.Errorf("Error reading %v: %v", abs, err) return nil
} }
err = f.Close()
if err != nil { func formatMQSCOutput(out string) string {
log.Errorf("Failed to close MQSC file handle: %v", err) // redact sensitive information
} out, _ = mqscredact.Redact(out)
err = stdin.Close()
if err != nil { // add tab characters to make it more readable as part of the log
log.Errorf("Failed to close MQSC stdin: %v", err) return strings.Replace(string(out), "\n", "\n\t", -1)
} }
// Run the command and wait for completion
out, err := cmd.CombinedOutput() func isStandbyQueueManager(name string) (bool, error) {
if err != nil { out, rc, err := command.Run("dspmq", "-n", "-m", name)
log.Println(err) if err != nil {
} log.Printf("Error %v getting status for queue manager %v: %v", rc, name, string(out))
// Print the runmqsc output, adding tab characters to make it more readable as part of the log return false, err
log.Printf("Output for \"runmqsc\" with %v:\n\t%v", abs, strings.Replace(string(out), "\n", "\n\t", -1)) }
if strings.Contains(string(out), "(RUNNING AS STANDBY)") {
return true, nil
}
return false, nil
}
func getQueueManagerDataDir(mounts map[string]string, name string) string {
dataDir := filepath.Join("/var/mqm/qmgrs", name)
if _, ok := mounts["/mnt/mqm-data"]; ok {
dataDir = filepath.Join("/mnt/mqm-data/qmgrs", name)
}
return dataDir
}
func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bool) []string {
args := []string{"-ii", "/etc/mqm/", "-ic", "/etc/mqm/", "-q", "-p", "1414"}
if devMode {
args = append(args, "-oa", "user")
}
if _, ok := mounts["/mnt/mqm-log"]; ok {
args = append(args, "-ld", "/mnt/mqm-log/log")
}
if _, ok := mounts["/mnt/mqm-data"]; ok {
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
}
args = append(args, name)
return args
}
func getCreateStandbyQueueManagerArgs(name string) []string {
args := []string{"-s", "QueueManager"}
args = append(args, "-v", fmt.Sprintf("Name=%v", name))
args = append(args, "-v", fmt.Sprintf("Directory=%v", name))
args = append(args, "-v", "Prefix=/var/mqm")
args = append(args, "-v", fmt.Sprintf("DataPath=/mnt/mqm-data/qmgrs/%v", name))
return args
}
// updateQMini removes the original ServicecCmponent stanza so we can add a new one
func updateQMini(qmname string) error {
val, set := os.LookupEnv("MQ_CONNAUTH_USE_HTP")
if !set {
//htpasswd mode not enabled.
return nil
}
bval, err := strconv.ParseBool(strings.ToLower(val))
if err != nil {
return err
}
if bval == false {
//htpasswd mode not enabled.
return nil
}
log.Printf("Removing existing ServiceComponent configuration")
mounts, err := containerruntime.GetMounts()
if err != nil {
log.Printf("Error getting mounts for queue manager")
return err
}
dataDir := getQueueManagerDataDir(mounts, qmname)
qmgrDir := filepath.Join(dataDir, "qm.ini")
//read the initial version.
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
iniFileBytes, err := ioutil.ReadFile(qmgrDir)
if err != nil {
return err
}
qminiConfigStr := string(iniFileBytes)
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
curFile := re.ReplaceAllString(qminiConfigStr, "")
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
err := ioutil.WriteFile(qmgrDir, []byte(curFile), 0660)
if err != nil {
return err
} }
} }
return nil return nil
} }
func stopQueueManager(name string) error {
log.Println("Stopping queue manager")
out, _, err := command.Run("endmqm", "-w", name)
if err != nil {
log.Printf("Error stopping queue manager: %v", string(out))
return err
}
log.Println("Stopped queue manager")
return nil
}

View File

@@ -1,137 +0,0 @@
/*
© Copyright IBM Corporation 2018
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os/user"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
)
const groupName string = "supplgrp"
func verifyCurrentUser() error {
log.Debug("Verifying current user information")
curUser, err := user.Current()
if err != nil {
return err
}
log.Debugf("Detected current user as: %v+", curUser)
if curUser.Username == "mqm" {
// Not supported yet
return fmt.Errorf("Container is running as mqm user which is not supported. Please run this container as root")
} else if curUser.Username == "root" {
// We're running as root so need to check for supplementary groups.
// We can't use the golang User.GroupIDs as it doesn't seem to detect container supplementary groups..
groups, err := getCurrentUserGroups()
for _, e := range groups {
_, _, testGroup := command.Run("getent", "group", e)
if testGroup != nil {
log.Printf("Group %s does not exist on the system... Adding to system and MQM user", e)
_, _, err = command.Run("groupadd", "-g", e, groupName)
if err != nil {
log.Errorf("Failed to create group %s as %s", e, groupName)
return err
}
_, _, err = command.Run("usermod", "-aG", groupName, "mqm")
if err != nil {
log.Errorf("Failed to add group %s(%s) to the mqm user.", groupName, e)
return err
}
}
}
} else {
// We're running as an unknown user...
return fmt.Errorf("Container is running as %s user which is not supported. Please run this container as root", curUser.Username)
}
return nil
}
func logUser() {
u, usererr := user.Current()
if usererr == nil {
g, err := getCurrentUserGroups()
if err != nil && len(g) == 0 {
log.Printf("Running as user ID %v (%v) with primary group %v", u.Uid, u.Name, u.Gid)
} else {
// Look for the primary group in the list of group IDs
for i, v := range g {
if v == u.Gid {
// Remove the element from the slice
g = append(g[:i], g[i+1:]...)
}
}
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.Uid, u.Name, u.Gid, strings.Join(g, ","))
}
}
if usererr == nil && u.Username != "mqm" {
mqm, err := user.Lookup("mqm")
// Need to print out mqm user details as well.
g, err := getUserGroups(mqm)
if err != nil && len(g) == 0 {
log.Printf("MQM user ID %v (%v) has primary group %v", mqm.Uid, "mqm", mqm.Gid)
} else {
// Look for the primary group in the list of group IDs
for i, v := range g {
if v == mqm.Gid {
// Remove the element from the slice
g = append(g[:i], g[i+1:]...)
}
}
log.Printf("MQM user ID %v (%v) has primary group %v, and supplementary groups %v", mqm.Uid, "mqm", mqm.Gid, strings.Join(g, ","))
}
}
}
func getCurrentUserGroups() ([]string, error) {
var nilArray []string
out, _, err := command.Run("id", "--groups")
if err != nil {
log.Debug("Unable to get current user groups")
return nilArray, err
}
out = strings.TrimSpace(out)
if out == "" {
// we don't have any groups?
return nilArray, fmt.Errorf("Unable to determine groups for current user")
}
groups := strings.Split(out, " ")
return groups, nil
}
func getUserGroups(usr *user.User) ([]string, error) {
var nilArray []string
out, _, err := command.Run("id", "--groups", usr.Uid)
if err != nil {
log.Debugf("Unable to get user %s groups", usr.Uid)
return nilArray, err
}
out = strings.TrimSpace(out)
if out == "" {
// we don't have any groups?
return nilArray, fmt.Errorf("Unable to determine groups for user %s", usr.Uid)
}
groups := strings.Split(out, " ")
return groups, nil
}

View File

@@ -29,6 +29,8 @@ var (
ImageRevision = "Not specified" ImageRevision = "Not specified"
// ImageSource is the URL to get source code for building the image // ImageSource is the URL to get source code for building the image
ImageSource = "Not specified" ImageSource = "Not specified"
// ImageTag is the tag of the image
ImageTag = "Not specified"
) )
func logDateStamp() { func logDateStamp() {
@@ -36,11 +38,15 @@ func logDateStamp() {
} }
func logGitRepo() { func logGitRepo() {
log.Printf("Image revision: %v", ImageRevision) // log.Printf("Image revision: %v", ImageRevision)
} }
func logGitCommit() { func logGitCommit() {
log.Printf("Image source: %v", ImageSource) // log.Printf("Image source: %v", ImageSource)
}
func logImageTag() {
log.Printf("Image tag: %v", ImageTag)
} }
func logMQVersion() { func logMQVersion() {
@@ -67,5 +73,6 @@ func logVersionInfo() {
logDateStamp() logDateStamp()
logGitRepo() logGitRepo()
logGitCommit() logGitCommit()
logImageTag()
logMQVersion() logMQVersion()
} }

View File

@@ -1,7 +1,5 @@
// +build mqdev
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,36 +17,42 @@ package main
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"syscall" "strings"
"github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/internal/copy"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
"github.com/ibm-messaging/mq-container/internal/tls"
) )
func startWebServer() error { func startWebServer(webKeystore, webkeystorePW, webTruststoreRef string) error {
_, err := os.Stat("/opt/mqm/bin/strmqweb") _, err := os.Stat("/opt/mqm/bin/strmqweb")
if err != nil && os.IsNotExist(err) { if err != nil && os.IsNotExist(err) {
log.Debug("Skipping web server, because it's not installed") log.Debug("Skipping web server, because it's not installed")
return nil return nil
} }
log.Println("Starting web server") log.Println("Starting web server")
// #nosec G204 - command is fixed, no injection vector
cmd := exec.Command("strmqweb") cmd := exec.Command("strmqweb")
// Set a default app password for the web server, if one isn't already set // Set a default app password for the web server, if one isn't already set
_, set := os.LookupEnv("MQ_APP_PASSWORD") _, set := os.LookupEnv("MQ_APP_PASSWORD")
if !set { if !set {
// Take all current environment variables, and add the app password // Take all current environment variables, and add the app password
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd") cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
} else {
cmd.Env = os.Environ()
} }
cmd.SysProcAttr = &syscall.SysProcAttr{}
uid, gid, err := command.LookupMQM() // TLS enabled
if err != nil { if webKeystore != "" {
return err cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webKeystore)
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+webkeystorePW)
cmd.Env = append(cmd.Env, "AMQ_WEBTRUSTSTOREREF="+webTruststoreRef)
} }
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} out, err := cmd.CombinedOutput()
out, rc, err := command.RunCmd(cmd) rc := cmd.ProcessState.ExitCode()
if err != nil { if err != nil {
log.Printf("Error %v starting web server: %v", rc, string(out)) log.Printf("Error %v starting web server: %v", rc, string(out))
return err return err
@@ -57,45 +61,84 @@ func startWebServer() error {
return nil return nil
} }
// CopyFile copies the specified file func configureSSO(p12TrustStore tls.KeyStoreData, webKeystore string) (string, error) {
func CopyFile(src, dest string) error { // Ensure all required environment variables are set for SSO
log.Debugf("Copying file %v to %v", src, dest) requiredEnvVars := []string{
in, err := os.Open(src) "MQ_OIDC_CLIENT_ID",
if err != nil { "MQ_OIDC_CLIENT_SECRET",
return err "MQ_OIDC_UNIQUE_USER_IDENTIFIER",
"MQ_OIDC_AUTHORIZATION_ENDPOINT",
"MQ_OIDC_TOKEN_ENDPOINT",
"MQ_OIDC_JWK_ENDPOINT",
"MQ_OIDC_ISSUER_IDENTIFIER",
} }
defer in.Close() for _, envVar := range requiredEnvVars {
if len(os.Getenv(envVar)) == 0 {
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0770) return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
defer out.Close() }
_, err = io.Copy(out, in)
if err != nil {
return err
} }
err = out.Close()
return err
}
func configureWebServer() error { // Check mqweb directory exists
_, err := os.Stat("/opt/mqm/bin/strmqweb") const mqwebDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb"
_, err := os.Stat(mqwebDir)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return "", nil
} }
return err return "", err
}
// Process SSO template for generating file mqwebuser.xml
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), "\n")
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
if err != nil {
return "", err
}
// Configure SSO TLS
return tls.ConfigureWebKeystore(p12TrustStore, webKeystore)
}
func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string, error) {
var webKeystore string
// Configure TLS for Web Console first if we have a certificate to use
err := tls.ConfigureWebTLS(keyLabel)
if err != nil {
return "", err
}
if keyLabel != "" {
webKeystore = keyLabel + ".p12"
}
// Configure Single-Sign-On for the web server (if enabled)
enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO")
if enableSSO == "true" || enableSSO == "1" {
webKeystore, err = configureSSO(p12Truststore, webKeystore)
if err != nil {
return "", err
}
} else if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") != "" {
webKeystore, err = tls.ConfigureWebKeystore(p12Truststore, webKeystore)
if err != nil {
return "", err
}
}
_, err = os.Stat("/opt/mqm/bin/strmqweb")
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
} }
const webConfigDir string = "/etc/mqm/web" const webConfigDir string = "/etc/mqm/web"
_, err = os.Stat(webConfigDir) _, err = os.Stat(webConfigDir)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return "", nil
} }
return err return "", err
}
uid, gid, err := command.LookupMQM()
if err != nil {
return err
} }
const prefix string = "/etc/mqm/web" const prefix string = "/etc/mqm/web"
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error { err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
@@ -114,6 +157,7 @@ func configureWebServer() error {
} }
if info.IsDir() { if info.IsDir() {
if !exists { if !exists {
// #nosec G301 - write group permissions are required
err := os.MkdirAll(to, 0770) err := os.MkdirAll(to, 0770)
if err != nil { if err != nil {
return err return err
@@ -126,17 +170,14 @@ func configureWebServer() error {
return err return err
} }
} }
err := CopyFile(from, to) err := copy.CopyFile(from, to)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return err return err
} }
} }
err = os.Chown(to, uid, gid)
if err != nil {
return err
}
return nil return nil
}) })
return err
return webKeystore, err
} }

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2017 # © Copyright IBM Corporation 2020
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
FROM mqadvanced FROM fedora:32
RUN useradd alice -G mqm && \ RUN yum install skopeo -y -qq
echo alice:passw0rd | chpasswd ENTRYPOINT [ "skopeo" ]
COPY *.mqsc /etc/mqm/

View File

@@ -2,46 +2,25 @@
## Prerequisites ## Prerequisites
### Prerequisites for building an Ubuntu image You need to have the following tools installed:
If you want to build a container image with Ubuntu Linux as the base OS, then you need to have the following tools installed:
* [Docker](https://www.docker.com/) V17.06.1 or later * [Docker](https://www.docker.com/) V17.06.1 or later, or [Podman](https://podman.io) V1.0 or later
* [GNU make](https://www.gnu.org/software/make/) * [GNU make](https://www.gnu.org/software/make/)
If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first. If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first.
### Prerequisites for building a Red Hat Enterprise Linux image You will also need a [Red Hat Account](https://access.redhat.com) to be able to access the Red Hat Registry.
If you want to build a container image with Red Hat Enterprise Linux as the base OS, then you need to use a host server with Red Hat Enterprise Linux. You must also have the following tools installed:
* [`buildah`](https://buildah.io) (available in `rhel-7-server-extras`)
* [`podman`](https://podman.io) (available in `rhel-7-server-extras`)
In addition, you need the following commonly installed tools:
* `bash`
* `coreutils`
* `findutils`
* `make`
* `sed`
* `shadow-utils`
* `tar`
## Building a production image ## Building a production image
This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures.
This procedure works for building the MQ Continuous Delivery release, on `amd64`, `ppc64le` and `s390x` architectures.
1. Create a `downloads` directory in the root of this repository 1. Create a `downloads` directory in the root of this repository
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.1.1_UBUNTU_X86-64.tar.gz` for MQ V9.1.1 for Ubuntu on x86_64 architecture) in the `downloads` directory 2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.2.0_LINUX_X86-64_NOINST.tar.gz`) in the `downloads` directory
3. Run `make build-advancedserver` 3. Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials.
4. Run `make build-advancedserver`
> **Warning**: Note that MQ offers two different sets of packaging on Linux: one is called "MQ for Linux" and contains RPM files for installing on Red Hat Enterprise Linux and SUSE Linux Enterprise Server. The other package is called "MQ for Ubuntu", and contains DEB files for installing on Ubuntu. > **Warning**: Note that from MQ 9.2.X, the MQ container build uses a 'No-Install' MQ Package, available under `IBM MQ V9.2.x Continuous Delivery Release components eAssembly, part no. CJ7CNML`
On a Red Hat Enterprise Linux host, the command `make build-advancedserver` will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
You can build a different version of MQ by setting the `MQ_VERSION` environment variable, for example:
```bash
MQ_VERSION=9.1.0.0 make build-advancedserver
```
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example: If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
@@ -50,10 +29,12 @@ MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
``` ```
## Building a developer image ## Building a developer image
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `x86_64` architecture. On a Red Hat Enterprise Linux host, this command will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
Login to the Red Hat Registry: `docker login registry.redhat.io` using your Customer Portal credentials.
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `amd64` architecture.
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory). You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
## Installed components ## Installed components
This image includes the core MQ server, Java, language packs, and GSKit. This can be configured by setting the `MQ_PACKAGES` argument to `make`. For the Ubuntu-based image, you can also directly set a [Docker build argument](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg). This image includes the core MQ server, Java, language packs, GSKit, and web server. This is configured in the `Generate MQ package in INSTALLATION_DIR` section [here](../install-mq.sh), with the configured options being picked up at build time.

View File

@@ -9,14 +9,12 @@ The MQ Developer Defaults supports some customization options, these are all con
* **MQ_DEV** - Set this to `false` to stop the default objects being created. * **MQ_DEV** - Set this to `false` to stop the default objects being created.
* **MQ_ADMIN_PASSWORD** - Changes the password of the `admin` user. Must be at least 8 characters long. * **MQ_ADMIN_PASSWORD** - Changes the password of the `admin` user. Must be at least 8 characters long.
* **MQ_APP_PASSWORD** - Changes the password of the app user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long. * **MQ_APP_PASSWORD** - Changes the password of the app user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long.
* **MQ_TLS_KEYSTORE** - Allows you to supply the location of a PKCS#12 keystore containing a single certificate which you want to use in both the web console and the queue manager. Requires `MQ_TLS_PASSPHRASE`. When enabled the channels created will be secured using the `TLS_RSA_WITH_AES_128_CBC_SHA256` CipherSpec. *Note*: you will need to make the keystore available inside your container, this can be done by mounting a volume to your container.
* **MQ_TLS_PASSPHRASE** - Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
## Details of the default configuration ## Details of the default configuration
The following users are created: The following users are created:
* User **admin** for administration (in the `mqm` group). Default password is **passw0rd**. * User **admin** for administration. Default password is **passw0rd**.
* User **app** for messaging (in a group called `mqclient`). No password by default. * User **app** for messaging (in a group called `mqclient`). No password by default.
Users in `mqclient` group have been given access connect to all queues and topics starting with `DEV.**` and have `put`, `get`, `pub`, `sub`, `browse` and `inq` permissions. Users in `mqclient` group have been given access connect to all queues and topics starting with `DEV.**` and have `put`, `get`, `pub`, `sub`, `browse` and `inq` permissions.
@@ -34,11 +32,6 @@ Two channels are created, one for administration, the other for normal messaging
* DEV.ADMIN.SVRCONN - configured to only allow the `admin` user to connect into it. A user and password must be supplied. * DEV.ADMIN.SVRCONN - configured to only allow the `admin` user to connect into it. A user and password must be supplied.
* DEV.APP.SVRCONN - does not allow administrative users to connect. Password is optional unless you choose a password for app users. * DEV.APP.SVRCONN - does not allow administrative users to connect. Password is optional unless you choose a password for app users.
A new listener is created (the SYSTEM listener is fine, but system objects are not shown by default in the web console):
* DEV.LISTENER.TCP - listens on port 1414.
## Web Console ## Web Console
By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to https://<Container IP>:9443/ibmmq/console. Where <Container IP> is replaced by the IP address of your running container. By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to https://<Container IP>:9443/ibmmq/console. Where <Container IP> is replaced by the IP address of your running container.
@@ -50,6 +43,6 @@ If you choose to accept the security warning, you will be presented with the log
* **User:** admin * **User:** admin
* **Password:** passw0rd * **Password:** passw0rd
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable. If you supply a PKCS#12 keystore using the `MQ_TLS_KEYSTORE` environment variable, then the web console will be configured to use the certificate inside the keystore for HTTPS operations. If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable.
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_DISABLE_WEB_CONSOLE` to `true`. If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_ENABLE_EMBEDDED_WEB_SERVER` to `false`.

View File

@@ -24,6 +24,7 @@ The `runmqserver` command has the following responsibilities:
- Works as PID 1, so is responsible for [reaping zombie processes](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/) - Works as PID 1, so is responsible for [reaping zombie processes](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
* Creating and starting a queue manager * Creating and starting a queue manager
* Configuring the queue manager, by running any MQSC scripts found under `/etc/mqm` * Configuring the queue manager, by running any MQSC scripts found under `/etc/mqm`
* Starts the MQ web server (if enabled)
* Starting Prometheus metrics generation for the queue manager (if enabled) * Starting Prometheus metrics generation for the queue manager (if enabled)
* Indicates to the `chkmqready` command that configuration is complete, and that normal readiness checking can happen. This is done by writing a file into `/run/runmqserver` * Indicates to the `chkmqready` command that configuration is complete, and that normal readiness checking can happen. This is done by writing a file into `/run/runmqserver`
@@ -36,8 +37,6 @@ The `runmqdevserver` command is added to the MQ Advanced for Developers image on
2. Generates MQSC files to put in `/etc/mqm`, based on a template, which is updated with values based on supplied environment variables. 2. Generates MQSC files to put in `/etc/mqm`, based on a template, which is updated with values based on supplied environment variables.
3. If requested, it creates TLS key stores under `/run/runmqdevserver`, and configures MQ and the web server to use them 3. If requested, it creates TLS key stores under `/run/runmqdevserver`, and configures MQ and the web server to use them
A special version of `runmqserver` is used in the developer image, which performs extra actions like starting the web server. This is built using the `mqdev` [build constraint](https://golang.org/pkg/go/build/#hdr-Build_Constraints).
## Prometheus metrics ## Prometheus metrics
[Prometheus](https://prometheus.io) metrics are generated for the queue manager as follows: [Prometheus](https://prometheus.io) metrics are generated for the queue manager as follows:

View File

@@ -0,0 +1,29 @@
### Queue Manager Connection Authentication using a htpasswd file
This pluggable authentication mode is to allow developers using the mq-container developer image to define users and their credentials into a .htpasswd file. This is in addition to the existing methods of MQ Connection Authentication (`CONNAUTH`) using Operating System or LDAP users.
**Please note:**
1. This new feature is enabled only when environment variable `--env MQ_CONNAUTH_USE_HTP=true` is set while creating a container.
2. When enabled, the `AuthType` value of the ConnectionAuthentication (`CONNAUTH`) is ignored and htpasswd mode is used. However, the MQ authority records created using (`SETMQAUT` or `AUTHREC`) will be in effect while using the htpasswd mode.
3. Channel Authentication records (`CHLAUTH`) will be in effect while using the htpasswd mode.
4. Passwords should be encrypted using bcrypt (golang.org/x/crypto/bcrypt).
5. This is developer only feature and not recommended for use in Production.
### Preparing htpasswd file
1. A default `mq.htpasswd` file is provided and placed under /etc/mqm/ directory inside the container.
2. You can set the password for user `admin` by setting the environment variable `MQ_ADMIN_PASSWORD`.
3. You can add user `app` into mq.htpasswd file by setting the environment variable `MQ_APP_PASSWORD`. This user `app` can be used to access `DEV.*` objects of the queue manager.
#### Next Steps:
Use an administrative tool or your application to connect to queue manager using the credentials defined in the mq.htpasswd file.
**Please note**: When an authentication request is made with a userid that is not defined in the `mq.htpasswd` file, then the authentication process is delegated to queue manager to handle. This will then use `IDPWOS` or `LDAP` modes for further processing.
#### Troubleshooting
A log file named `amqpasdev.log` is generated under `/var/mqm/errors` directory path of the container. This file will contain all the failed connection authentication requests.
**Please note**: This log file is based on circular logging and the maximum size is restricted to 1MB.

35
docs/security.md Normal file
View File

@@ -0,0 +1,35 @@
# Security
## Container runtime
### User
The MQ server image is run using with UID 1001, though this can be any UID, with a fixed GID of 0 (root).
### Capabilities
The MQ Advanced image requires no Linux capabilities, so you can drop any capabilities which are added by default. For example, in Docker you could do the following:
```sh
docker run \
--cap-drop=ALL \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--detach \
ibm-mqadvanced-server:9.2.0.0-amd64
```
The MQ Advanced for Developers image does require the "chown", "setuid", "setgid" and "audit_write" capabilities (plus "dac_override" if you're using an image based on Red Hat Enterprise Linux). This is because it uses the "sudo" command to change passwords inside the container. For example, in Docker, you could do the following:
```sh
docker run \
--cap-drop=ALL \
--cap-add=CHOWN \
--cap-add=SETUID \
--cap-add=SETGID \
--cap-add=AUDIT_WRITE \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--detach \
ibm-mqadvanced-server-dev:9.2.0.0-amd64
```

View File

@@ -6,7 +6,6 @@ You need to ensure you have the following tools installed:
* [GNU make](https://www.gnu.org/software/make/) * [GNU make](https://www.gnu.org/software/make/)
* [Go](https://golang.org/) - only needed for running the tests * [Go](https://golang.org/) - only needed for running the tests
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests * [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
* [Helm](https://helm.sh) - only needed for running the Kubernetes tests
## Running the tests ## Running the tests
There are two main sets of tests: There are two main sets of tests:
@@ -15,29 +14,30 @@ There are two main sets of tests:
2. Docker tests, which test a complete Docker image, using the Docker API 2. Docker tests, which test a complete Docker image, using the Docker API
### Running the Docker tests ### Running the Docker tests
The Docker tests can be run locally on a machine with Docker. For example:
The Docker tests can be run locally on a machine with Docker. For example:
``` ```
make test-devserver make devserver
make test-advancedserver make advancedserver
``` ```
You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example: You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example:
``` ```
MQ_IMAGE_ADVANCEDSERVER=mqadvanced-server:9.1.1.0-x86_64-ubuntu-16.04 make test-advancedserver MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.2.0.0-amd64 make test-advancedserver
``` ```
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command:: You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command:
``` ```
TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver
``` ```
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `mqadvanced-server:9.1.0.0-x86_64-ubuntu-16.04`: You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `ibm-mqadvanced-server:9.2.0.0-amd64`:
``` ```
MQ_VERSION=9.1.0.0 make test-advancedserver MQ_VERSION=9.2.0.0 make test-advancedserver
``` ```
### Running the Docker tests with code coverage ### Running the Docker tests with code coverage
@@ -49,12 +49,3 @@ make test-advancedserver-cover
``` ```
In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory. In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory.
### Running the Kubernetes tests
For the Kubernetes tests, you need to have built the Docker image, and pushed it to the registry used by your Kubernetes cluster. Most of the configuration used by the tests is picked up from your `kubectl` configuration, but you will typically need to specify the image details. For example:
```bash
MQ_IMAGE=mycluster.icp:8500/default/mq-devserver make test-kubernetes-devserver
```

View File

@@ -2,6 +2,8 @@
In order to use the image, it is necessary to accept the terms of the IBM MQ license. This is achieved by specifying the environment variable `LICENSE` equal to `accept` when running the image. You can also view the license terms by setting this variable to `view`. Failure to set the variable will result in the termination of the container with a usage statement. You can view the license in a different language by also setting the `LANG` environment variable. In order to use the image, it is necessary to accept the terms of the IBM MQ license. This is achieved by specifying the environment variable `LICENSE` equal to `accept` when running the image. You can also view the license terms by setting this variable to `view`. Failure to set the variable will result in the termination of the container with a usage statement. You can view the license in a different language by also setting the `LANG` environment variable.
> **Note**: You can use `podman` instead of `docker` in any of the examples on this page.
## Running with the default configuration ## Running with the default configuration
You can run a queue manager with the default configuration and a listener on port 1414 using the following command. For example, the following command creates and starts a queue manager called `QM1`, and maps port 1414 on the host to the MQ listener on port 1414 inside the container, as well as port 9443 on the host to the web console on port 9443 inside the container: You can run a queue manager with the default configuration and a listener on port 1414 using the following command. For example, the following command creates and starts a queue manager called `QM1`, and maps port 1414 on the host to the MQ listener on port 1414 inside the container, as well as port 9443 on the host to the web console on port 9443 inside the container:
@@ -58,28 +60,23 @@ You can customize the configuration in several ways:
1. For getting started, you can use the [default developer configuration](developer-config.md), which is available out-of-the-box for the MQ Advanced for Developers image 1. For getting started, you can use the [default developer configuration](developer-config.md), which is available out-of-the-box for the MQ Advanced for Developers image
2. By creating your own image and adding your own MQSC file into the `/etc/mqm` directory on the image. This file will be run when your queue manager is created. 2. By creating your own image and adding your own MQSC file into the `/etc/mqm` directory on the image. This file will be run when your queue manager is created.
3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.1.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer. 3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer.
Note that a listener is always created on port 1414 inside the container. This port can be mapped to any port on the Docker host. Note that a listener is always created on port 1414 inside the container. This port can be mapped to any port on the Docker host.
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file, and an administrative user `alice`. Note that it is not normally recommended to include passwords in this way: The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file:
```dockerfile ```dockerfile
FROM ibmcom/mq FROM ibmcom/mq
RUN useradd alice -G mqm && \ USER 1001
echo alice:passw0rd | chpasswd
COPY 20-config.mqsc /etc/mqm/ COPY 20-config.mqsc /etc/mqm/
``` ```
Here is an example corresponding `20-config.mqsc` script from the [mqdev blog](https://developer.ibm.com/messaging/2018/10/01/archives-getting-going-without-turning-off-ibm-mq-security/), which allows users with passwords to connect on the `PASSWORD.SVRCONN` channel: Here is an example corresponding `20-config.mqsc` script, which creates two local queues:
```mqsc ```mqsc
DEFINE CHANNEL(PASSWORD.SVRCONN) CHLTYPE(SVRCONN) REPLACE DEFINE QLOCAL(MY.QUEUE.1) REPLACE
SET CHLAUTH(PASSWORD.SVRCONN) TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allow privileged users on this channel') DEFINE QLOCAL(MY.QUEUE.2) REPLACE
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('BackStop rule')
SET CHLAUTH(PASSWORD.SVRCONN) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(REQUIRED)
ALTER AUTHINFO(SYSTEM.DEFAULT.AUTHINFO.IDPWOS) AUTHTYPE(IDPWOS) ADOPTCTX(YES)
REFRESH SECURITY TYPE(CONNAUTH)
``` ```
The file `20-config.mqsc` should be saved into the same directory as the `Dockerfile`. The file `20-config.mqsc` should be saved into the same directory as the `Dockerfile`.
@@ -96,3 +93,22 @@ docker exec \
``` ```
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes. Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
## Supplying TLS certificates
If you wish to supply TLS Certificates that the queue manager and MQ Console should use for TLS operations then you must supply a PKCS#1 or unencrypted PKCS#8 PEM files for both the certificates and private keys in the following directories:
* `/etc/mqm/pki/keys/<Label>` - for certificates with public and private keys
* `/etc/mqm/pki/trust/<index>` - for certificates with only the public key
For example, if you have an identity certificate you wish to add with the label `mykey` and 2 certificates you wish to add as trusted then you would need to add the files into the following locations where files ending in `.key` contain private keys and `.crt` contain certificates:
- `/etc/mqm/pki/keys/mykey/tls.key`
- `/etc/mqm/pki/keys/mykey/tls.crt`
- `/etc/mqm/pki/keys/mykey/ca.crt`
- `/etc/mqm/pki/trust/0/tls.crt`
- `/etc/mqm/pki/trust/1/tls.crt`
This can be achieved by either mounting the directories or files into the container when you run it or by baking the files into the correct location in the image.
If you supply multiple identity certificates then the first label alphabetically will be chosen as the certificate to be used by the MQ Console and the default certificate for the queue manager. If you wish to use a different certificate on the queue manager then you can change the certificate to use at runtime by executing the MQSC command `ALTER QMGR CERTLABL('<newlabel>')`

View File

@@ -1,4 +1,5 @@
* © Copyright IBM Corporation 2017 * © Copyright IBM Corporation 2019
*
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -12,4 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
REFRESH SECURITY TYPE(CONNAUTH) * Set the keystore location for the queue manager
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
REFRESH SECURITY(*) TYPE(SSL)

2
etc/mqm/mq.htpasswd Normal file
View File

@@ -0,0 +1,2 @@
admin:$2y$05$M/C1U62RZ6q1kv4E7.S7ueNESJmFe85RsZcoMUReRXUDB8QcP3yqS
app:$2y$05$BnbPtcjXTjk5JRJ8gzHqIuHgoQbLF3qtbPV3Q3tLyr0XJNg.7dkxW

View File

@@ -0,0 +1,11 @@
ServiceComponent:
Service=AuthorizationService
Name=Dev.HtpAuth.Service
Module=/opt/mqm/lib64/amqpasdev.so
ComponentDataSize=0
ServiceComponent:
Service=AuthorizationService
Name=MQSeries.UNIX.auth.service
Module=amqzfu
ComponentDataSize=0

53
glide.lock generated
View File

@@ -1,53 +0,0 @@
hash: b02555ebf3957ece0ae5ecf132fa4e415a4f66a7f4c27a82d484f4fb78f56e41
updated: 2018-07-13T08:50:32.923040349+01:00
imports:
- name: github.com/beorn7/perks
version: 3a771d992973f24aa725d07868b467d1ddfceafb
subpackages:
- quantile
- name: github.com/genuinetools/amicontained
version: fcae88544f0212fbb1e20699c41566655b68679b
subpackages:
- container
- name: github.com/golang/protobuf
version: 70b3af33377e7aa25ae42977bed93cc6b90f0373
subpackages:
- proto
- name: github.com/ibm-messaging/mq-golang
version: 1b2a2ad95ba3c555944be28097d392c27bda4071
subpackages:
- ibmmq
- mqmetric
- name: github.com/matttproud/golang_protobuf_extensions
version: c12348ce28de40eed0136aa2b644d0ee0650e56c
subpackages:
- pbutil
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
subpackages:
- prometheus
- name: github.com/prometheus/client_model
version: 5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f
subpackages:
- go
- name: github.com/prometheus/common
version: 7600349dcfe1abd18d72d3a1770870d9800a7801
subpackages:
- expfmt
- internal/bitbucket.org/ww/goautoneg
- model
- name: github.com/prometheus/procfs
version: ae68e2d4c00fed4943b5f6698d504a5fe083da8a
subpackages:
- internal/util
- nfs
- xfs
- name: github.com/syndtr/gocapability
version: 33e07d32887e1e06b7c025f27ce52f62c7990bc0
subpackages:
- capability
- name: golang.org/x/sys
version: 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
subpackages:
- unix
testImports: []

View File

@@ -1,28 +0,0 @@
# © Copyright IBM Corporation 2017
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package: github.com/ibm-messaging/mq-container
license: Apache-2.0
excludeDirs:
- build
- coverage
- test
import:
- package: golang.org/x/sys/unix
- package: github.com/prometheus/client_golang
version: 0.8.0
- package: github.com/ibm-messaging/mq-golang
version: 2.0.0
- package: github.com/genuinetools/amicontained
version: 0.4.0

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2017 # © Copyright IBM Corporation 2015, 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -20,9 +20,11 @@ ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messag
# The MQ packages to install # The MQ packages to install
ARG MQ_PACKAGES="ibmmq-sfbridge" ARG MQ_PACKAGES="ibmmq-sfbridge"
ARG MQM_UID=999
ADD install-mq.sh /usr/local/bin/ ADD install-mq.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/install-mq.sh \ RUN chmod u+x /usr/local/bin/install-mq.sh \
&& install-mq.sh && install-mq.sh $MQM_UID
ENV LANG=en_US.UTF-8 ENV LANG=en_US.UTF-8

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2017 # © Copyright IBM Corporation 2015, 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,27 +12,33 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
FROM ubuntu:16.04 FROM registry.redhat.io/ubi8/ubi-minimal AS mq-explorer
# The URL to download the MQ installer from in tar.gz format # The URL to download the MQ installer from in tar.gz format
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz"
# The MQ packages to install # The MQ packages to install
ARG MQ_PACKAGES="ibmmq-explorer" ENV MQ_PACKAGES="MQSeriesRuntime*.rpm MQSeriesJRE*.rpm MQSeriesExplorer*.rpm"
RUN export DEBIAN_FRONTEND=noninteractive \ ARG MQM_UID=888
&& apt-get update \
&& apt-get install -y \ RUN microdnf install -y gtk2 libXtst \
libgtk2.0-0 \ && microdnf clean all
libxtst6
ADD install-mq.sh /usr/local/bin/ ADD install-mq.sh /usr/local/bin/
# Install MQ Explorer. To avoid a "text file busy" error here, we sleep before installing.
# Need to re-instate the `/var/mqm` directory after installation, to avoid MQ
# errors with some commands (e.g. `dspmqver`)
RUN chmod u+x /usr/local/bin/install-mq.sh \ RUN chmod u+x /usr/local/bin/install-mq.sh \
&& install-mq.sh && sleep 1 \
&& install-mq.sh $MQM_UID \
&& rm -rf /var/mqm \
&& /opt/mqm/bin/crtmqdir -f -s
ENV LANG=en_US.UTF-8 ENV LANG=en_US.UTF-8
# Run as mqm (999) # Run as mqm
USER 999 USER $MQM_UID
ENTRYPOINT ["MQExplorer"] ENTRYPOINT ["MQExplorer"]

View File

@@ -1,33 +0,0 @@
# © Copyright IBM Corporation 2018
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ARG BASE_IMAGE=mq-sdk:9.1.1.0-x86_64-ubuntu-16.04
FROM $BASE_IMAGE
COPY incubating/mq-golang-sdk/install-golang.sh /usr/local/bin
ENV GO_VERSION=1.10
ENV PATH="${PATH}:/usr/lib/go-${GO_VERSION}/bin:/go/bin:/usr/local/go/bin" \
CGO_CFLAGS="-I/opt/mqm/inc/" \
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
GOPATH="/go"
# Install the Go compiler and Git
RUN chmod +x /usr/local/bin/install-golang.sh \
&& sleep 1 \
&& install-golang.sh
WORKDIR $GOPATH

View File

@@ -1,5 +0,0 @@
# IBM MQ Software Developer Kit (SDK) with Go
This image contains the MQ SDK, Git, the Go compiler, and the `build-essential` package (which includes GNU C and C++ compilers plus other essential tools like `make`).
This image doesn't contain any Go code for MQ. You can add a CGO wrapper for the MQ C client, for example [mq-golang](https://github.com/ibm-messaging/mq-golang), via your vendor directory, or directly using `go get`.

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2018, 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,17 +12,19 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
ARG BASE_IMAGE=ubuntu:16.04 FROM registry.redhat.io/rhel8/llvm-toolset:8.0.1-10 AS mq-sdk
#FROM docker.io/centos/devtoolset-7-toolchain-centos7 AS mq-sdk
FROM $BASE_IMAGE
# The URL to download the MQ installer from in tar.gz format # The URL to download the MQ installer from in tar.gz format
# This assumes an archive containing the MQ Debian (.deb) install packages # This assumes an archive containing the MQ Debian (.deb) install packages
ARG MQ_URL ARG MQ_URL
# The packages to install in install-mq.sh # The packages to install in install-mq.sh
ARG MQ_PACKAGES ENV MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm"
ENV MQM_UID=888
USER 0
COPY install-mq.sh /usr/local/bin/ COPY install-mq.sh /usr/local/bin/
# Install MQ. To avoid a "text file busy" error here, we sleep before installing. # Install MQ. To avoid a "text file busy" error here, we sleep before installing.
@@ -30,6 +32,7 @@ COPY install-mq.sh /usr/local/bin/
# errors with some commands (e.g. `dspmqver`) # errors with some commands (e.g. `dspmqver`)
RUN chmod u+x /usr/local/bin/install-mq.sh \ RUN chmod u+x /usr/local/bin/install-mq.sh \
&& sleep 1 \ && sleep 1 \
&& install-mq.sh \ && install-mq.sh $MQM_UID \
&& rm -rf /var/mqm \ && rm -rf /var/mqm \
&& /opt/mqm/bin/crtmqdir -f -s && /opt/mqm/bin/crtmqdir -f -s
USER 1001

View File

@@ -1,4 +1,4 @@
* © Copyright IBM Corporation 2017, 2018 * © Copyright IBM Corporation 2017, 2019
* *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
STOP LISTENER('SYSTEM.LISTENER.TCP.1')
* Developer queues * Developer queues
DEFINE QLOCAL('DEV.QUEUE.1') REPLACE DEFINE QLOCAL('DEV.QUEUE.1') REPLACE
DEFINE QLOCAL('DEV.QUEUE.2') REPLACE DEFINE QLOCAL('DEV.QUEUE.2') REPLACE
@@ -42,12 +40,9 @@ SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('Back-sto
SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT({{ .ChckClnt }}) DESCR('Allows connection via APP channel') ACTION(REPLACE) SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT({{ .ChckClnt }}) DESCR('Allows connection via APP channel') ACTION(REPLACE)
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE) SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE)
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE) SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE)
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(MAP) MCAUSER ('mqm') DESCR ('Allow admin as MQ-admin') ACTION(REPLACE)
* Developer authority records * Developer authority records
SET AUTHREC GROUP('mqclient') OBJTYPE(QMGR) AUTHADD(CONNECT,INQ) SET AUTHREC PRINCIPAL('app') OBJTYPE(QMGR) AUTHADD(CONNECT,INQ)
SET AUTHREC PROFILE('DEV.**') GROUP('mqclient') OBJTYPE(QUEUE) AUTHADD(BROWSE,GET,INQ,PUT) SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(QUEUE) AUTHADD(BROWSE,GET,INQ,PUT)
SET AUTHREC PROFILE('DEV.**') GROUP('mqclient') OBJTYPE(TOPIC) AUTHADD(PUB,SUB) SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(TOPIC) AUTHADD(PUB,SUB)
* Developer listener
DEFINE LISTENER('DEV.LISTENER.TCP') TRPTYPE(TCP) PORT(1414) CONTROL(QMGR) REPLACE
START LISTENER('DEV.LISTENER.TCP')

View File

@@ -1,4 +1,4 @@
* © Copyright IBM Corporation 2018 * © Copyright IBM Corporation 2018, 2019
* *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* Set the keystore location for the queue manager
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
* Set the cipherspec for dev channels * Set the cipherspec for dev channels
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL) ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL) ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)

View File

@@ -1,65 +0,0 @@
# © Copyright IBM Corporation 2015, 2018
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ARG BASE_IMAGE=mqadvanced-server-dev-base:9.1.1.0-x86_64-ubuntu-16.04
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.1.0-x86_64-ubuntu-16.04
###############################################################################
# Build stage to build Go code
###############################################################################
FROM $BUILDER_IMAGE as builder
ARG IMAGE_REVISION="Not specified"
ARG IMAGE_SOURCE="Not specified"
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
COPY cmd/ ./cmd
COPY internal/ ./internal
COPY vendor/ ./vendor
# Re-build runmqserver, with code tagged with 'mqdev' enabled
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" --tags 'mqdev' ./cmd/runmqserver
RUN go build ./cmd/runmqdevserver/
# Run all unit tests
RUN go test -v ./cmd/runmqdevserver/...
###############################################################################
# Main build stage
###############################################################################
FROM $BASE_IMAGE
# Enable MQ developer default configuration
ENV MQ_DEV=true
# Default administrator password
ENV MQ_ADMIN_PASSWORD=passw0rd
## Add admin and app users, and set a default password for admin
RUN useradd admin -G mqm \
&& groupadd mqclient \
&& useradd app -G mqclient \
&& echo admin:$MQ_ADMIN_PASSWORD | chpasswd
# Create a directory for runtime data from runmqserver
RUN mkdir -p /run/runmqdevserver \
&& chown mqm:mqm /run/runmqdevserver
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqdevserver /usr/local/bin/
# Copy template files
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
# Copy web XML files for default developer configuration
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
RUN chmod +x /usr/local/bin/runmq*
EXPOSE 9443
ENTRYPOINT ["runmqdevserver"]

View File

@@ -1,17 +0,0 @@
#!/bin/bash
# Change admin password
if [ -n "${MQ_ADMIN_PASSWORD}" ]; then
echo admin:${MQ_ADMIN_PASSWORD} | chpasswd
fi
# Change app password
if [ -n "${MQ_APP_PASSWORD}" ]; then
echo app:${MQ_APP_PASSWORD} | chpasswd
fi
# Delete the MQSC with developer defaults, if requested
if [ "${MQ_DEV}" != "true" ]; then
rm -f /etc/mqm/dev.mqsc
fi
exec runmqserver

View File

@@ -0,0 +1,41 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2019
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Fail on any non-zero return code
set -ex
test -f /usr/bin/yum && YUM=true || YUM=false
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
if ($UBUNTU); then
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends sudo
rm -rf /var/lib/apt/lists/*
fi
if ($YUM); then
yum -y install sudo
yum -y clean all
rm -rf /var/cache/yum/*
fi
if ($MICRODNF); then
microdnf install sudo
microdnf clean all
fi

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<server>
<keyStore id="MQWebKeyStore" location="/run/runmqdevserver/tls/key.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
<keyStore id="MQWebTrustStore" location="/run/runmqdevserver/tls/trust.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
<ssl id="thisSSLConfig" clientAuthenticationSupported="true" keyStoreRef="MQWebKeyStore" trustStoreRef="MQWebTrustStore" sslProtocol="TLSv1.2" serverKeyAlias="devcert"/>
<sslDefault sslRef="thisSSLConfig"/>
</server>

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2018 # © Copyright IBM Corporation 2015, 2019
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,8 +19,8 @@
set -ex set -ex
curl https://glide.sh/get | sh sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.1/dep-linux-$ARCH
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64
sudo chmod +x /usr/local/bin/dep sudo chmod +x /usr/local/bin/dep
go get golang.org/x/lint/golint go get -u golang.org/x/lint/golint
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin 2.0.0 || echo "Gosec not installed. Platform may not be supported."

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2015, 2020
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,9 @@
# Fail on any non-zero return code # Fail on any non-zero return code
set -ex set -ex
test -f /usr/bin/yum && RHEL=true || RHEL=false test -f /usr/bin/yum && YUM=true || YUM=false
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
test -f /usr/bin/rpm && RPM=true || RPM=false
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
if ($UBUNTU); then if ($UBUNTU); then
@@ -37,37 +39,43 @@ if ($UBUNTU); then
# This ensures no unsupported code gets installed, and makes the build faster # This ensures no unsupported code gets installed, and makes the build faster
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-backports main restricted universe" >> /etc/apt/sources.list;
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
apt-get update
apt-get install -y --no-install-recommends \
golang-${GO_VERSION} \
git \
ca-certificates
fi
if ($RHEL); then
# Install additional packages required by MQ, this install process and the runtime scripts # Install additional packages required by MQ, this install process and the runtime scripts
yum -y install \ apt-get update
git \ apt-get install -y --no-install-recommends \
bash \
bc \
ca-certificates \
coreutils \
curl \ curl \
debianutils \
file \
findutils \
gawk \
grep \
libc-bin \
mount \
passwd \
procps \
sed \
tar \ tar \
gcc util-linux
cd /tmp
curl -LO https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz
tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
fi fi
# Remove any orphaned packages if ($RPM); then
$UBUNTU && apt-get autoremove -y EXTRA_RPMS="bash bc ca-certificates file findutils gawk glibc-common grep ncurses-compat-libs passwd procps-ng sed shadow-utils tar util-linux which"
# Install additional packages required by MQ, this install process and the runtime scripts
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
$MICRODNF && microdnf install ${EXTRA_RPMS}
fi
# Apply any bug fixes not included in base Ubuntu or MQ image.
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 perl-base --only-upgrade
# End of bug fixes
# Clean up cached files # Clean up cached files
$UBUNTU && rm -rf /var/lib/apt/lists/* $UBUNTU && rm -rf /var/lib/apt/lists/*
$RHEL && yum -y clean all $YUM && yum -y clean all
$RHEL && rm -rf /var/cache/yum/* $YUM && rm -rf /var/cache/yum/*
$MICRODNF && microdnf clean all
# Make the GOLANG directories
mkdir -p $GOPATH/src $GOPATH/bin
chmod -R 777 $GOPATH

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2018 # © Copyright IBM Corporation 2015, 2020
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,154 +18,91 @@
# Fail on any non-zero return code # Fail on any non-zero return code
set -ex set -ex
test -f /usr/bin/yum && RHEL=true || RHEL=false test -f /usr/bin/rpm && RPM=true || RPM=false
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults # Only install the SDK package as part of the build stage
if [ -z "$MQ_PACKAGES" ]; then INSTALL_SDK=${INSTALL_SDK:-0}
$UBUNTU && MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams"
$RHEL && MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm"
fi
if ($UBUNTU); then # Download and extract the MQ unzippable server
export DEBIAN_FRONTEND=noninteractive DIR_TMP=/tmp/mq
# Use a reduced set of apt repositories. mkdir -p ${DIR_TMP}
# This ensures no unsupported code gets installed, and makes the build faster cd ${DIR_TMP}
source /etc/os-release
# Figure out the correct apt URL based on the CPU architecture
CPU_ARCH=$(uname -p)
if [ ${CPU_ARCH} == "x86_64" ]; then
APT_URL="http://archive.ubuntu.com/ubuntu/"
else
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
fi
# Use a reduced set of apt repositories.
# This ensures no unsupported code gets installed, and makes the build faster
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
# Install additional packages required by MQ, this install process and the runtime scripts
apt-get update
apt-get install -y --no-install-recommends \
bash \
bc \
ca-certificates \
coreutils \
curl \
debianutils \
file \
findutils \
gawk \
grep \
libc-bin \
mount \
passwd \
procps \
sed \
tar \
util-linux
fi
# Install additional packages required by MQ, this install process and the runtime scripts
$RHEL && yum -y install \
bash \
bc \
ca-certificates \
coreutils \
curl \
file \
findutils \
gawk \
glibc-common \
grep \
passwd \
procps-ng \
sed \
tar \
util-linux
# Download and extract the MQ installation files
DIR_EXTRACT=/tmp/mq
mkdir -p ${DIR_EXTRACT}
cd ${DIR_EXTRACT}
curl -LO $MQ_URL curl -LO $MQ_URL
tar -zxvf ./*.tar.gz
# Remove packages only needed by this script tar -xzf ./*.tar.gz
$UBUNTU && apt-get purge -y \ rm -f ./*.tar.gz
ca-certificates \ ls -la ${DIR_TMP}
curl
# Note: ca-certificates and curl are installed by default in RHEL # Generate MQ package in INSTALLATION_DIR
export genmqpkg_inc32=0
# Remove any orphaned packages export genmqpkg_incadm=1
$UBUNTU && apt-get autoremove -y export genmqpkg_incamqp=0
export genmqpkg_incams=1
# Recommended: Create the mqm user ID with a fixed UID and group, so that the file permissions work between different images export genmqpkg_inccbl=0
$UBUNTU && groupadd --system --gid 999 mqm export genmqpkg_inccics=0
$UBUNTU && useradd --system --uid 999 --gid mqm mqm export genmqpkg_inccpp=0
$RHEL && groupadd --system --gid 888 mqm export genmqpkg_incdnet=0
$RHEL && useradd --system --uid 888 --gid mqm mqm export genmqpkg_incjava=1
usermod -aG mqm root export genmqpkg_incjre=1
export genmqpkg_incman=0
# Find directory containing .deb files export genmqpkg_incmqbc=0
$UBUNTU && DIR_DEB=$(find ${DIR_EXTRACT} -name "*.deb" -printf "%h\n" | sort -u | head -1) export genmqpkg_incmqft=0
$RHEL && DIR_RPM=$(find ${DIR_EXTRACT} -name "*.rpm" -printf "%h\n" | sort -u | head -1) export genmqpkg_incmqsf=0
# Find location of mqlicense.sh export genmqpkg_incmqxr=0
MQLICENSE=$(find ${DIR_EXTRACT} -name "mqlicense.sh") export genmqpkg_incnls=1
export genmqpkg_incras=1
export genmqpkg_incsamp=1
export genmqpkg_incsdk=$INSTALL_SDK
export genmqpkg_inctls=1
export genmqpkg_incunthrd=0
export genmqpkg_incweb=1
export INSTALLATION_DIR=/opt/mqm
${DIR_TMP}/bin/genmqpkg.sh -b ${INSTALLATION_DIR}
ls -la ${INSTALLATION_DIR}
rm -rf ${DIR_TMP}
# Accept the MQ license # Accept the MQ license
${MQLICENSE} -text_only -accept ${INSTALLATION_DIR}/bin/mqlicense -accept
$UBUNTU && echo "deb [trusted=yes] file:${DIR_DEB} ./" > /etc/apt/sources.list.d/IBM_MQ.list
# Install MQ using the DEB packages
$UBUNTU && apt-get update
$UBUNTU && apt-get install -y $MQ_PACKAGES
$RHEL && cd $DIR_RPM && rpm -ivh $MQ_PACKAGES
# Remove 32-bit libraries from 64-bit container
find /opt/mqm /var/mqm -type f -exec file {} \; | awk -F: '/ELF 32-bit/{print $1}' | xargs --no-run-if-empty rm -f
# Remove tar.gz files unpacked by RPM postinst scripts
find /opt/mqm -name '*.tar.gz' -delete
# Recommended: Set the default MQ installation (makes the MQ commands available on the PATH)
/opt/mqm/bin/setmqinst -p /opt/mqm -i
# Clean up all the downloaded files
$UBUNTU && rm -f /etc/apt/sources.list.d/IBM_MQ.list
rm -rf ${DIR_EXTRACT}
# Apply any bug fixes not included in base Ubuntu or MQ image.
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 --only-upgrade
# End of bug fixes
# Clean up cached files
$UBUNTU && rm -rf /var/lib/apt/lists/*
$RHEL && yum -y clean all
$RHEL && rm -rf /var/cache/yum/*
# Optional: Update the command prompt with the MQ version # Optional: Update the command prompt with the MQ version
$UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot $UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
# Remove the directory structure under /var/mqm which was created by the installer # Create the mount point for volumes, ensuring MQ has permissions to all directories
rm -rf /var/mqm install --directory --mode 2775 --owner 1001 --group root /mnt
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm
# Create the mount point for volumes install --directory --mode 2775 --owner 1001 --group root /mnt/mqm/data
mkdir -p /mnt/mqm install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-log
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-log/log
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-data
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-data/qmgrs
# Create the directory for MQ configuration files # Create the directory for MQ configuration files
mkdir -p /etc/mqm install --directory --mode 2775 --owner 1001 --group root /etc/mqm
# Create the directory for MQ runtime files
install --directory --mode 2775 --owner 1001 --group root /run/mqm
# Create a symlink for /var/mqm -> /mnt/mqm/data # Create a symlink for /var/mqm -> /mnt/mqm/data
ln -s /mnt/mqm/data /var/mqm ln -s /mnt/mqm/data /var/mqm
# Optional: Set these values for the Bluemix Vulnerability Report # Optional: Ensure any passwords expire in a timely manner
sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' /etc/login.defs sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' /etc/login.defs
sed -i 's/PASS_MIN_DAYS\t0/PASS_MIN_DAYS\t1/' /etc/login.defs sed -i 's/PASS_MIN_DAYS\t0/PASS_MIN_DAYS\t1/' /etc/login.defs
sed -i 's/PASS_MIN_LEN\t5/PASS_MIN_LEN\t8/' /etc/login.defs
$RPM && sed -i 's/# minlen/minlen/' /etc/security/pwquality.conf
$UBUNTU && PAM_FILE=/etc/pam.d/common-password $UBUNTU && PAM_FILE=/etc/pam.d/common-password
$RHEL && PAM_FILE=/etc/pam.d/password-auth $RPM && PAM_FILE=/etc/pam.d/password-auth
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE
# List all the installed packages, for the build log
$RPM && rpm -q --all || true
$UBUNTU && dpkg --list || true
#Update the license file to include UBI 8 instead of UBI 7
sed -i 's/v7.0/v8.0/g' /opt/mqm/licenses/non_ibm_license.txt
# Copy MQ Licenses into the correct location
mkdir -p /licenses
cp /opt/mqm/licenses/*.txt /licenses/

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,69 +20,20 @@ package command
import ( import (
"fmt" "fmt"
"os/exec" "os/exec"
"os/user"
"runtime"
"strconv"
"syscall"
) )
// RunCmd runs an OS command. On Linux it waits for the command to
// complete and returns the exit status (return code).
// Do not use this function to run shell built-ins (like "cd"), because
// the error handling works differently
func RunCmd(cmd *exec.Cmd) (string, int, error) {
// Run the command and wait for completion
out, err := cmd.CombinedOutput()
if err != nil {
// Assert that this is an ExitError
exiterr, ok := err.(*exec.ExitError)
// If the type assertion was correct, and we're on Linux
if ok && runtime.GOOS == "linux" {
status, ok := exiterr.Sys().(syscall.WaitStatus)
if ok {
return string(out), status.ExitStatus(), fmt.Errorf("%v: %v", cmd.Path, err)
}
}
return string(out), -1, err
}
return string(out), 0, nil
}
// Run runs an OS command. On Linux it waits for the command to // Run runs an OS command. On Linux it waits for the command to
// complete and returns the exit status (return code). // complete and returns the exit status (return code).
// Do not use this function to run shell built-ins (like "cd"), because // Do not use this function to run shell built-ins (like "cd"), because
// the error handling works differently // the error handling works differently
func Run(name string, arg ...string) (string, int, error) { func Run(name string, arg ...string) (string, int, error) {
// #nosec G204 // Run the command and wait for completion
return RunCmd(exec.Command(name, arg...))
}
// RunAsMQM runs the specified command as the mqm user
func RunAsMQM(name string, arg ...string) (string, int, error) {
// #nosec G204 // #nosec G204
cmd := exec.Command(name, arg...) cmd := exec.Command(name, arg...)
cmd.SysProcAttr = &syscall.SysProcAttr{} out, err := cmd.CombinedOutput()
uid, gid, err := LookupMQM() rc := cmd.ProcessState.ExitCode()
if err != nil { if err != nil {
return "", 0, err return string(out), rc, fmt.Errorf("%v: %v", cmd.Path, err)
} }
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} return string(out), rc, nil
return RunCmd(cmd)
}
// LookupMQM looks up the UID & GID of the mqm user
func LookupMQM() (int, int, error) {
mqm, err := user.Lookup("mqm")
if err != nil {
return -1, -1, err
}
mqmUID, err := strconv.Atoi(mqm.Uid)
if err != nil {
return -1, -1, err
}
mqmGID, err := strconv.Atoi(mqm.Gid)
if err != nil {
return -1, -1, err
}
return mqmUID, mqmGID, nil
} }

View File

@@ -0,0 +1,128 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package containerruntime
import (
"fmt"
"io/ioutil"
"strings"
"github.com/genuinetools/amicontained/container"
)
func GetContainerRuntime() (string, error) {
return container.DetectRuntime()
}
func GetBaseImage() (string, error) {
buf, err := ioutil.ReadFile("/etc/os-release")
if err != nil {
return "", fmt.Errorf("Failed to read /etc/os-release: %v", err)
}
lines := strings.Split(string(buf), "\n")
for _, l := range lines {
if strings.HasPrefix(l, "PRETTY_NAME=") {
words := strings.Split(l, "\"")
if len(words) >= 2 {
return words[1], nil
}
}
}
return "unknown", nil
}
// GetCapabilities gets the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
func GetCapabilities() (map[string][]string, error) {
return container.Capabilities()
}
// GetSeccomp gets the seccomp enforcing mode, which affects which kernel calls can be made
func GetSeccomp() (string, error) {
s, err := container.SeccompEnforcingMode()
if err != nil {
return "", fmt.Errorf("Failed to get container SeccompEnforcingMode: %v", err)
}
return s, nil
}
// GetSecurityAttributes gets the security attributes of the current process.
// The security attributes indicate whether AppArmor or SELinux are being used,
// and what the level of confinement is.
func GetSecurityAttributes() string {
a, err := readProc("/proc/self/attr/current")
// On some systems, if AppArmor or SELinux are not installed, you get an
// error when you try and read `/proc/self/attr/current`, even though the
// file exists.
if err != nil || a == "" {
a = "none"
}
return a
}
func readProc(filename string) (value string, err error) {
// #nosec G304
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(buf)), nil
}
func GetMounts() (map[string]string, error) {
all, err := readProc("/proc/mounts")
if err != nil {
return nil, fmt.Errorf("Couldn't read /proc/mounts")
}
result := make(map[string]string)
lines := strings.Split(all, "\n")
for i := range lines {
parts := strings.Split(lines[i], " ")
//dev := parts[0]
mountPoint := parts[1]
fsType := parts[2]
if strings.Contains(mountPoint, "/mnt/mqm") {
result[mountPoint] = fsType
}
}
return result, nil
}
func GetKernelVersion() (string, error) {
return readProc("/proc/sys/kernel/osrelease")
}
func GetMaxFileHandles() (string, error) {
return readProc("/proc/sys/fs/file-max")
}
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
func SupportedFilesystem(fsType string) bool {
switch fsType {
case "aufs", "overlayfs", "tmpfs":
return false
default:
return true
}
}
// ValidMultiInstanceFilesystem returns true if the supplied filesystem type is valid for a multi-instance queue manager
func ValidMultiInstanceFilesystem(fsType string) bool {
if !SupportedFilesystem(fsType) {
return false
}
// TODO : check for non-shared filesystems & shared filesystems which are known not to work
return true
}

View File

@@ -1,7 +1,7 @@
// +build linux // +build linux
/* /*
© Copyright IBM Corporation 2017, 2018 © Copyright IBM Corporation 2017, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -15,11 +15,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main package containerruntime
import ( import (
"fmt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -101,24 +99,17 @@ var fsTypes = map[int64]string{
0x58295829: "zsmalloc", 0x58295829: "zsmalloc",
} }
func checkFS(path string) error { // GetFilesystem returns the filesystem type for the specified path
func GetFilesystem(path string) (string, error) {
statfs := &unix.Statfs_t{} statfs := &unix.Statfs_t{}
err := unix.Statfs(path, statfs) err := unix.Statfs(path, statfs)
if err != nil { if err != nil {
log.Println(err) return "", err
return nil
} }
// Use a type conversion to make type an int64. On s390x it's a uint32. // Use a type conversion to make type an int64. On s390x it's a uint32.
t, ok := fsTypes[int64(statfs.Type)] t, ok := fsTypes[int64(statfs.Type)]
if !ok { if !ok {
log.Printf("WARNING: detected %v has unknown filesystem type %x", path, statfs.Type) return "unknown", nil
return nil
}
switch t {
case "aufs", "overlayfs", "tmpfs":
return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
default:
log.Printf("Detected %v has filesystem type '%v'", path, t)
return nil
} }
return t, nil
} }

View File

@@ -1,7 +1,7 @@
// +build !linux // +build !linux
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main package containerruntime
// Dummy version of this function, only for non-Linux systems. // Dummy version of this function, only for non-Linux systems.
// Having this allows unit tests to be run on other platforms (e.g. macOS) // Having this allows unit tests to be run on other platforms (e.g. macOS)

58
internal/copy/copy.go Normal file
View File

@@ -0,0 +1,58 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package copy
import (
"fmt"
"io"
"os"
"github.com/ibm-messaging/mq-container/internal/filecheck"
)
func CopyFileMode(src, dest string, perm os.FileMode) error {
err := filecheck.CheckFileSource(src)
if err != nil {
return fmt.Errorf("failed to open %s for copy: %v", src, err)
}
// #nosec G304 - filename variable 'src' is checked above to ensure it is valid
in, err := os.Open(src)
if err != nil {
return fmt.Errorf("failed to open %s for copy: %v", src, err)
}
defer in.Close()
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
if err != nil {
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
err = out.Close()
return err
}
// CopyFile copies the specified file
func CopyFile(src, dest string) error {
return CopyFileMode(src, dest, 0770)
}

View File

@@ -1,7 +1,5 @@
// +build !mqdev
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -15,8 +13,25 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main
func postInit(name string) error { package filecheck
import (
"fmt"
"path/filepath"
"strings"
)
// CheckFileSource checks the filename is valid
func CheckFileSource(fileName string) error {
absFile, _ := filepath.Abs(fileName)
prefixes := []string{"bin", "boot", "dev", "lib", "lib64", "proc", "sbin", "sys"}
for _, prefix := range prefixes {
if strings.HasPrefix(absFile, filepath.Join("/", prefix)) {
return fmt.Errorf("Filename resolves to invalid path '%v'", absFile)
}
}
return nil return nil
} }

View File

@@ -0,0 +1,40 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filecheck
import (
"testing"
)
func TestCheckFileSource(t *testing.T) {
invalidFilenames := []string{"/bin", "/boot", "/dev", "/lib", "/lib64", "/proc", "/sbin", "/sys", "/bin/myfile", "/boot/mydir/myfile", "/var/../dev", "/var/../lib/myfile"}
for _, invalidFilename := range invalidFilenames {
err := CheckFileSource(invalidFilename)
if err == nil {
t.Errorf("Expected to receive an error for filename '%v'", invalidFilename)
}
}
validFilenames := []string{"/var", "/mydir/dev", "/mydir/dev/myfile"}
for _, validFilename := range validFilenames {
err := CheckFileSource(validFilename)
if err != nil {
t.Errorf("Unexpected error received: %v", err)
}
}
}

View File

@@ -0,0 +1,153 @@
/*
© Copyright IBM Corporation 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//This is a developer only configuration and not recommended for production usage.
package htpasswd
import (
"fmt"
"io/ioutil"
"strings"
"golang.org/x/crypto/bcrypt"
)
type mapHtPasswd map[string]string
func encryptPassword(password string) (string, error) {
passwordBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(passwordBytes), nil
}
// SetPassword sets encrypted password for the user into htpasswd file
func SetPassword(user string, password string, isTest bool) error {
if len(strings.TrimSpace(user)) == 0 || len(strings.TrimSpace(password)) == 0 {
return fmt.Errorf("UserId or Password are empty")
}
passwords := mapHtPasswd(map[string]string{})
// Read the password file
err := passwords.ReadHtPasswordFile(isTest)
if err != nil {
return err
}
pwd, err := encryptPassword(password)
if err != nil {
return err
}
// Set the new password
passwords[user] = pwd
// Update the password file
return passwords.updateHtPasswordFile(isTest)
}
// GetBytes return the Bytes representation of the htpassword file
func (htpfile mapHtPasswd) GetBytes() (passwordBytes []byte) {
passwordBytes = []byte{}
for name, hash := range htpfile {
passwordBytes = append(passwordBytes, []byte(name+":"+hash+"\n")...)
}
return passwordBytes
}
// ReadHtPasswordFile parses the htpasswd file
func (htpfile mapHtPasswd) ReadHtPasswordFile(isTest bool) error {
file := "/etc/mqm/mq.htpasswd"
if isTest {
file = "my.htpasswd"
}
pwdsBytes, err := ioutil.ReadFile(file)
if err != nil {
return err
}
lines := strings.Split(string(pwdsBytes), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
parts := strings.Split(line, ":")
if len(parts) != 2 {
continue
}
for i, part := range parts {
parts[i] = strings.TrimSpace(part)
}
htpfile[parts[0]] = parts[1]
}
return nil
}
func (htpfile mapHtPasswd) updateHtPasswordFile(isTest bool) error {
file := "/etc/mqm/mq.htpasswd"
if isTest {
file = "my.htpasswd"
}
return ioutil.WriteFile(file, htpfile.GetBytes(), 0660)
}
// AuthenticateUser verifies if the given user password match with htpasswrd
func AuthenticateUser(user string, password string, isTest bool) (bool, bool, error) {
passwords := mapHtPasswd(map[string]string{})
if len(strings.TrimSpace(user)) == 0 || len(strings.TrimSpace(password)) == 0 {
return false, false, fmt.Errorf("UserId or Password are empty")
}
err := passwords.ReadHtPasswordFile(isTest)
if err != nil {
return false, false, err
}
ok := false
value, found := passwords[user]
if !found {
return found, ok, fmt.Errorf("User not found in the mq.htpasswd file")
}
err = bcrypt.CompareHashAndPassword([]byte(value), []byte(password))
return found, err == nil, err
}
// ValidateUser validates the given user
func ValidateUser(user string, isTest bool) (bool, error) {
passwords := mapHtPasswd(map[string]string{})
if len(strings.TrimSpace(user)) == 0 {
return false, fmt.Errorf("Userid is empty for AuthenticateUser")
}
err := passwords.ReadHtPasswordFile(isTest)
if err != nil {
return false, err
}
_, found := passwords[strings.TrimSpace(user)]
return found, nil
}

View File

@@ -0,0 +1,62 @@
/*
© Copyright IBM Corporation 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package htpasswd
import (
"testing"
)
// TestCheckUser verifies Htpassword's use
func TestCheckUser(t *testing.T) {
err := SetPassword("guest", "guestpw", true)
if err != nil {
t.Fatalf("htpassword test failed due to error:%s\n", err.Error())
}
found, ok, err := AuthenticateUser("guest", "guestpw", true)
if err != nil {
t.Fatalf("htpassword test1 failed as user could not be found:%s\n", err.Error())
}
if found == false || ok == false {
t.Fatalf("htpassword test1 failed as user could not be found:%v, ok:%v\n", found, ok)
}
found, ok, err = AuthenticateUser("myguest", "guestpw", true)
if err == nil {
t.Fatalf("htpassword test2 failed as no error received for non-existing user\n")
}
if found == true || ok == true {
t.Fatalf("htpassword test2 failed for non-existing user found :%v, ok:%v\n", found, ok)
}
found, ok, err = AuthenticateUser("guest", "guest", true)
if err == nil {
t.Fatalf("htpassword test3 failed as incorrect password of user did not return error\n")
}
if found == false || ok == true {
t.Fatalf("htpassword test3 failed for existing user with incorrect passwored found :%v, ok:%v\n", found, ok)
}
found, err = ValidateUser("guest", true)
if err != nil || found == false {
t.Fatalf("htpassword test4 failed as user could not be found:%v, ok:%v\n", found, ok)
}
found, err = ValidateUser("myguest", true)
if err != nil || found == true {
t.Fatalf("htpassword test5 failed as non-existing user returned to be found:%v, ok:%v\n", found, ok)
}
}

View File

@@ -0,0 +1 @@
guest:$2y$05$ifFP0nCmFed6.m4iB9CHRuHFps2YeeuwopmOvszWt0GRnN59p8qxW

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -13,12 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main
// Package keystore contains code to create and update keystores
package keystore
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -53,13 +56,22 @@ func NewCMSKeyStore(filename, password string) *KeyStore {
} }
} }
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
func NewPKCS12KeyStore(filename, password string) *KeyStore {
return &KeyStore{
Filename: filename,
Password: password,
keyStoreType: "p12",
command: "/opt/mqm/bin/runmqakm",
}
}
// Create a key store, if it doesn't already exist // Create a key store, if it doesn't already exist
func (ks *KeyStore) Create() error { func (ks *KeyStore) Create() error {
_, err := os.Stat(ks.Filename) _, err := os.Stat(ks.Filename)
if err == nil { if err == nil {
// Keystore already exists so we should refresh it by deleting it. // Keystore already exists so we should refresh it by deleting it.
extension := filepath.Ext(ks.Filename) extension := filepath.Ext(ks.Filename)
log.Debugf("Refreshing keystore: %v", ks.Filename)
if ks.keyStoreType == "cms" { if ks.keyStoreType == "cms" {
// Only delete these when we are refreshing the kdb keystore // Only delete these when we are refreshing the kdb keystore
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth" stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
@@ -67,23 +79,19 @@ func (ks *KeyStore) Create() error {
crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl" crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
err = os.Remove(stashFile) err = os.Remove(stashFile)
if err != nil { if err != nil {
log.Errorf("Error removing %s: %v", stashFile, err)
return err return err
} }
err = os.Remove(rdbFile) err = os.Remove(rdbFile)
if err != nil { if err != nil {
log.Errorf("Error removing %s: %v", rdbFile, err)
return err return err
} }
err = os.Remove(crlFile) err = os.Remove(crlFile)
if err != nil { if err != nil {
log.Errorf("Error removing %s: %v", crlFile, err)
return err return err
} }
} }
err = os.Remove(ks.Filename) err = os.Remove(ks.Filename)
if err != nil { if err != nil {
log.Errorf("Error removing %s: %v", ks.Filename, err)
return err return err
} }
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
@@ -97,16 +105,6 @@ func (ks *KeyStore) Create() error {
return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out) return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
} }
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(ks.Filename, mqmUID, mqmGID)
if err != nil {
log.Error(err)
return err
}
return nil return nil
} }
@@ -114,7 +112,6 @@ func (ks *KeyStore) Create() error {
func (ks *KeyStore) CreateStash() error { func (ks *KeyStore) CreateStash() error {
extension := filepath.Ext(ks.Filename) extension := filepath.Ext(ks.Filename)
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth" stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
log.Debugf("TLS stash file: %v", stashFile)
_, err := os.Stat(stashFile) _, err := os.Stat(stashFile)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -125,16 +122,6 @@ func (ks *KeyStore) CreateStash() error {
} }
return err return err
} }
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(stashFile, mqmUID, mqmGID)
if err != nil {
log.Error(err)
return err
}
return nil return nil
} }
@@ -147,6 +134,33 @@ func (ks *KeyStore) Import(inputFile, password string) error {
return nil return nil
} }
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) error {
out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth")
if err != nil {
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
}
return nil
}
// Add adds a CA certificate to the keystore
func (ks *KeyStore) Add(inputFile, label string) error {
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
if err != nil {
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
}
return nil
}
// Add adds a CA certificate to the keystore
func (ks *KeyStore) AddNoLabel(inputFile string) error {
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile)
if err != nil {
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
}
return nil
}
// GetCertificateLabels returns the labels of all certificates in the key store // GetCertificateLabels returns the labels of all certificates in the key store
func (ks *KeyStore) GetCertificateLabels() ([]string, error) { func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
@@ -171,9 +185,44 @@ func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
// RenameCertificate renames the specified certificate // RenameCertificate renames the specified certificate
func (ks *KeyStore) RenameCertificate(from, to string) error { func (ks *KeyStore) RenameCertificate(from, to string) error {
out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to) if ks.command == "/opt/mqm/bin/runmqakm" {
if err != nil { // runmqakm can't handle certs with ' in them so just use capicmd
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out) // Overriding gosec here as this function is in an internal package and only callable by our internal functions.
// #nosec G204
cmd := exec.Command("/opt/mqm/gskit8/bin/gsk8capicmd_64", "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
cmd.Env = append(os.Environ(), "LD_LIBRARY_PATH=/opt/mqm/gskit8/lib64/:/opt/mqm/gskit8/lib")
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", "/opt/mqm/gskit8/bin/gsk8capicmd_64", err, out)
}
} else {
out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
if err != nil {
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
}
} }
return nil return nil
} }
// ListAllCertificates Lists all certificates in the keystore
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
if err != nil {
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
}
scanner := bufio.NewScanner(strings.NewReader(out))
var labels []string
for scanner.Scan() {
s := scanner.Text()
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
s := strings.TrimLeft(s, "-*!")
labels = append(labels, strings.TrimSpace(s))
}
}
err = scanner.Err()
if err != nil {
return nil, err
}
return labels, nil
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ limitations under the License.
package metrics package metrics
import ( import (
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -23,7 +23,8 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-container/internal/ready"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
@@ -39,6 +40,15 @@ var (
// GatherMetrics gathers metrics for the queue manager // GatherMetrics gathers metrics for the queue manager
func GatherMetrics(qmName string, log *logger.Logger) { func GatherMetrics(qmName string, log *logger.Logger) {
// If running in standby mode - wait until the queue manager becomes active
for {
active, _ := ready.IsRunningAsActiveQM(qmName)
if active {
break
}
time.Sleep(requestTimeout * time.Second)
}
metricsEnabled = true metricsEnabled = true
err := startMetricsGathering(qmName, log) err := startMetricsGathering(qmName, log)

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-golang/ibmmq" "github.com/ibm-messaging/mq-golang/ibmmq"
"github.com/ibm-messaging/mq-golang/mqmetric" "github.com/ibm-messaging/mq-golang/mqmetric"
) )

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/ibm-messaging/mq-container/internal/logger" "github.com/ibm-messaging/mq-container/pkg/logger"
"github.com/ibm-messaging/mq-golang/mqmetric" "github.com/ibm-messaging/mq-golang/mqmetric"
) )

View File

@@ -0,0 +1,264 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mqscredact
import (
"bufio"
"io"
"regexp"
"strings"
)
/* List of sensitive MQ Parameters */
var sensitiveParameters = []string{"LDAPPWD", "PASSWORD", "SSLCRYP"}
// redactionString is what sensitive paramters will be replaced with
const redactionString = "(*********)"
func findEndOfParamterString(stringDenoter rune, r *bufio.Reader) string {
parameter := ""
for {
char, _, err := r.ReadRune()
if err != nil {
return parameter
}
parameter = parameter + string(char)
if char == stringDenoter {
break
} else if char == '\n' {
// Check if we're on a comment line
NewLineLoop:
for {
// Look at next character without moving buffer forwards
chars, err := r.Peek(1)
if err != nil {
return parameter
}
// Check if we're at the beginning of some data.
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(chars[0]))
if startOutput {
// We are at the start, check if we're on a comment line
if chars[0] == '*' {
// found a comment line. go to the next newline chraracter
CommentLoop:
for {
char, _, err = r.ReadRune()
if err != nil {
return parameter
}
parameter = parameter + string(char)
if char == '\n' {
break CommentLoop
}
}
// Go round again as we're now on a new line
continue NewLineLoop
}
// We've checked for comment and it isn't a comment line so break without moving buffer forwards
break NewLineLoop
}
// Move the buffer forward and try again
char, _, _ = r.ReadRune()
parameter = parameter + string(char)
}
}
}
return parameter
}
// getParameterString reads from r in order to find the end of the MQSC Parameter value. This is enclosed in ( ).
// This function will return what it finds and will increment the reader pointer along as it goes.
func getParameterString(r *bufio.Reader) string {
// Add the ( in as it will have been dropped before.
parameter := "("
Loop:
for {
char, _, err := r.ReadRune()
if err != nil {
return parameter
}
parameter = parameter + string(char)
switch char {
case ')':
break Loop
// TODO: Duplicate code..
case '\'', '"':
parameter = parameter + findEndOfParamterString(char, r)
}
}
return parameter
}
func resetAllParameters(currentVerb, originalString *string, lineContinuation, foundGap, parameterNext, redacting, checkComment *bool) {
*currentVerb = ""
*originalString = ""
*lineContinuation = false
*foundGap = false
*parameterNext = false
*redacting = false
*checkComment = true
}
// Redact is the main function for redacting sensitive parameters in MQSC strings
// It accepts a string and redacts sensitive paramters such as LDAPPWD or PASSWORD
func Redact(out string) (string, error) {
out = strings.TrimSpace(out)
var returnStr, currentVerb, originalString string
var lineContinuation, foundGap, parameterNext, redacting, checkComment bool
newline := true
resetAllParameters(&currentVerb, &originalString, &lineContinuation, &foundGap, &parameterNext, &redacting, &checkComment)
r := bufio.NewReader(strings.NewReader(out))
MainLoop:
for {
// We have found a opening ( so use special parameter parsing
if parameterNext {
parameterStr := getParameterString(r)
if !redacting {
returnStr = returnStr + parameterStr
} else {
returnStr = returnStr + redactionString
}
resetAllParameters(&currentVerb, &originalString, &lineContinuation, &foundGap, &parameterNext, &redacting, &checkComment)
}
// Loop round getting hte next parameter
char, _, err := r.ReadRune()
if err == io.EOF {
if originalString != "" {
returnStr = returnStr + originalString
}
break
} else if err != nil {
return returnStr, err
}
/* We need to push forward until we find a non-whitespace, digit or colon character */
if newline {
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(char))
if !startOutput {
originalString = originalString + string(char)
continue MainLoop
}
newline = false
}
switch char {
// Found a line continuation character
case '+', '-':
lineContinuation = true
foundGap = false
originalString = originalString + string(char)
continue MainLoop
// Found whitespace/new line
case '\n':
checkComment = true
newline = true
fallthrough
case '\t', '\r', ' ':
if !lineContinuation {
foundGap = true
}
originalString = originalString + string(char)
continue MainLoop
// Found a paramter value
case '(':
parameterNext = true
/* Do not continue as we need to do some checks */
// Found a comment, parse in a special manner
case '*':
if checkComment {
originalString = originalString + string(char)
// Loop round until we find the new line character that marks the end of the comment
CommentLoop:
for {
char, _, err := r.ReadRune()
if err == io.EOF {
if originalString != "" {
returnStr = returnStr + originalString
}
break MainLoop
} else if err != nil {
return returnStr, err
}
originalString = originalString + string(char)
if char == '\n' {
break CommentLoop
}
}
//Comment has been read and added to original string, go back to start
checkComment = true
newline = true
continue MainLoop
}
/* Do not continue as we need to do some checks */
} //end of switch
checkComment = false
if lineContinuation {
lineContinuation = false
}
if foundGap || parameterNext {
// we've completed an parameter so check whether it is sensitive
currentVerb = strings.ToUpper(currentVerb)
if isSensitiveCommand(currentVerb) {
redacting = true
}
// Add the unedited string to the return string
returnStr = returnStr + originalString
//reset some of the parameters
originalString = ""
currentVerb = ""
foundGap = false
lineContinuation = false
}
originalString = originalString + string(char)
currentVerb = currentVerb + string(char)
}
return returnStr, nil
}
// isSensitiveCommand checks whether the given string contains a sensitive parameter.
// We use contains here because we can't determine whether a line continuation seperates
// parts of a parameter or two different parameters.
func isSensitiveCommand(command string) bool {
for _, v := range sensitiveParameters {
if strings.Contains(command, v) {
return true
}
}
return false
}

View File

@@ -0,0 +1,171 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mqscredact
import (
"strings"
"testing"
)
const passwordString = passwordHalf1 + passwordHalf2
const passwordHalf1 = "hippo"
const passwordHalf2 = "123456"
var testStrings = [...]string{
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordString + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordString + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD ('" + passwordString + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t('" + passwordString + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd('" + passwordString + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD('" + passwordString + "')",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD('" + passwordString + "')",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordString + "\")",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD ('" + passwordString + "')",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t('" + passwordString + "')",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password('" + passwordString + "')",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD('" + passwordString + "')",
"ALTER QMGR SSLCRYP('" + passwordString + "')",
"ALTER QMGR SSLCRYP(\"" + passwordString + "\")",
"ALTER QMGR SSLCRYP ('" + passwordString + "')",
"ALTER QMGR SSLCRYP\t\t('" + passwordString + "')",
"ALTER QMGR sslcryp('" + passwordString + "')",
"ALTER QMGR sslCRYP('" + passwordString + "')",
// Line continuation ones
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t" + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t " + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n " + passwordHalf2 + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t" + passwordHalf2 + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t " + passwordHalf2 + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\n" + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "-\n" + passwordHalf2 + "')",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+ \n " + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\t\n " + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "- \n" + passwordHalf2 + "\")",
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\t\n" + passwordHalf2 + "\")",
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
//edge cases
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 123+\n 456\")",
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n123-\n456\")",
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
"ALTER QMGR SSLCRYP + \n (\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
"ALTER QMGR SSLCRYP - \n(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
"ALTER QMGR SSL + \n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
"ALTER QMGR SSL - \nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
"ALTER QMGR + \n SSL +\n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\") +\n TEST(1234)",
"ALTER QMGR -\nSSL -\nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\") -\nTEST(1234)",
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP('" + passwordString + "')",
" 1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD('" + passwordString + "')",
" 2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ' \n" + passwordHalf2 + "')",
" 3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ') \n" + passwordHalf2 + "')",
}
var expected = [...]string{
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD " + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD " + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP " + redactionString,
"ALTER QMGR SSLCRYP\t\t" + redactionString,
"ALTER QMGR sslcryp" + redactionString,
"ALTER QMGR sslCRYP" + redactionString,
// Line continuation ones
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
//edge cases
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP" + redactionString,
"ALTER QMGR SSLCRYP + \n \t " + redactionString,
"ALTER QMGR SSLCRYP - \n " + redactionString,
"ALTER QMGR SSL + \n CRYP" + redactionString,
"ALTER QMGR SSL - \nCRYP" + redactionString,
"ALTER QMGR + \n SSL +\n CRYP" + redactionString + " +\n TEST(1234)",
"ALTER QMGR -\nSSL -\nCRYP" + redactionString + " -\nTEST(1234)",
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP" + redactionString,
"1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD" + redactionString,
"2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
"3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
}
// Returns true if the 2 strings are equal ignoring whitespace characters
func compareIgnoreWhiteSpace(str1, str2 string) bool {
whiteSpaces := [...]string{" ", "\t", "\n", "\r"}
for _, w := range whiteSpaces {
str1 = strings.Replace(str1, w, "", -1)
str2 = strings.Replace(str2, w, "", -1)
}
return str1 == str2
}
func TestAll(t *testing.T) {
for i, v := range testStrings {
back, _ := Redact(v)
if strings.Contains(back, passwordHalf1) || strings.Contains(back, passwordHalf2) || strings.Contains(back, passwordString) {
t.Errorf("MAJOR FAIL[%d]: Found an instance of the password. ", i)
}
if !compareIgnoreWhiteSpace(back, expected[i]) {
t.Errorf("FAIL[%d]:\nGave :%s\nexpected:%s\ngot :%s", i, v, expected[i], back)
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2020
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -13,19 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main
// Package mqtemplate contains code to process template files
package mqtemplate
import ( import (
"os" "os"
"path" "path"
"text/template" "text/template"
"github.com/ibm-messaging/mq-container/internal/command" "github.com/ibm-messaging/mq-container/pkg/logger"
) )
// processTemplateFile takes a Go templateFile, and processes it with the // ProcessTemplateFile takes a Go templateFile, and processes it with the
// supplied data, writing to destFile // supplied data, writing to destFile
func processTemplateFile(templateFile, destFile string, data interface{}) error { func ProcessTemplateFile(templateFile, destFile string, data interface{}, log *logger.Logger) error {
// Re-configure channel if app password not set // Re-configure channel if app password not set
t, err := template.ParseFiles(templateFile) t, err := template.ParseFiles(templateFile)
if err != nil { if err != nil {
@@ -36,17 +38,8 @@ func processTemplateFile(templateFile, destFile string, data interface{}) error
_, err = os.Stat(dir) _, err = os.Stat(dir)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = os.MkdirAll(dir, 0660) // #nosec G301
if err != nil { err = os.MkdirAll(dir, 0770)
log.Error(err)
return err
}
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(dir, mqmUID, mqmGID)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return err return err
@@ -63,15 +56,5 @@ func processTemplateFile(templateFile, destFile string, data interface{}) error
log.Error(err) log.Error(err)
return err return err
} }
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
log.Error(err)
return err
}
err = os.Chown(destFile, mqmUID, mqmGID)
if err != nil {
log.Error(err)
return err
}
return nil return nil
} }

299
internal/qmgrauth/pas.go Normal file
View File

@@ -0,0 +1,299 @@
/*
© Copyright IBM Corporation 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//This is a developer only configuration and not recommended for production usage.
package main
/*
#cgo !windows CFLAGS: -I/opt/mqm/lib64 -D_REENTRANT
#cgo !windows,!darwin LDFLAGS: -L/opt/mqm/lib64 -lmqm_r -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64
#cgo darwin LDFLAGS: -L/opt/mqm/lib64 -lmqm_r -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64
#cgo windows CFLAGS: -I"C:/Program Files/IBM/MQ/Tools/c/include"
#cgo windows LDFLAGS: -L "C:/Program Files/IBM/MQ/bin64" -lmqm
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <cmqxc.h>
#include <cmqzc.h>
#include <cmqec.h>
#include <time.h>
static MQZ_INIT_AUTHORITY PASStart;
static MQZ_AUTHENTICATE_USER OAAuthUser;
static MQZ_FREE_USER OAFreeUser;
static MQZ_TERM_AUTHORITY OATermAuth;
extern int Authenticate(char *, char *);
extern int CheckAuthority(char *);
static char *OAEnvStr(MQLONG);
static void FindSize();
static void PrintDateTime();
static FILE *fp = NULL;
static int primary_process = 0;
static void MQENTRY PASStart(
MQHCONFIG hc,
MQLONG Options,
MQCHAR48 QMgrName,
MQLONG ComponentDataLength,
PMQBYTE ComponentData,
PMQLONG Version,
PMQLONG pCompCode,
PMQLONG pReason) {
MQLONG CC = MQCC_OK;
MQLONG Reason = MQRC_NONE;
if ((Options & MQZIO_PRIMARY) == MQZIO_PRIMARY)
primary_process = 1;
fp=fopen("/var/mqm/errors/amqpasdev.log","a");
if (CC == MQCC_OK)
hc->MQZEP_Call(hc, MQZID_INIT_AUTHORITY,(PMQFUNC)PASStart,&CC,&Reason);
if (CC == MQCC_OK)
hc->MQZEP_Call(hc,MQZID_TERM_AUTHORITY,(PMQFUNC)OATermAuth,&CC,&Reason);
if (CC == MQCC_OK)
hc->MQZEP_Call(hc,MQZID_AUTHENTICATE_USER,(PMQFUNC)OAAuthUser,&CC,&Reason);
if (CC == MQCC_OK)
hc->MQZEP_Call(hc,MQZID_FREE_USER,(PMQFUNC)OAFreeUser,&CC,&Reason);
*Version = MQZAS_VERSION_5;
*pCompCode = CC;
*pReason = Reason;
PrintDateTime();
fprintf(fp, "Pluggable OAM Initialized.\n");
fprintf(fp, "THIS IS A DEVELOPER ONLY CONFIGURATION AND NOT RECOMMENDED FOR PRODUCTION USAGE");
return;
}
static char *authuserfmt =
"\tUser : \"%12.12s\"\n"\
"\tEffUser : \"%12.12s\"\n"\
"\tAppName : \"%28.28s\"\n"\
"\tApIdDt : \"%32.32s\"\n"\
"\tEnv : \"%s\"\n"\
"\tApp Pid : %d\n"\
"\tApp Tid : %d\n"\
;
static void MQENTRY OAAuthUser (
PMQCHAR pQMgrName,
PMQCSP pSecurityParms,
PMQZAC pApplicationContext,
PMQZIC pIdentityContext,
PMQPTR pCorrelationPtr,
PMQBYTE pComponentData,
PMQLONG pContinuation,
PMQLONG pCompCode,
PMQLONG pReason)
{
char *spuser = NULL;
char *sppass = NULL;
int gorc = MQRC_NOT_AUTHORIZED;
if ((pSecurityParms->CSPUserIdLength) > 0) {
//Grab the user creds from csp.
spuser = malloc(pSecurityParms->CSPUserIdLength+1);
strncpy(spuser,pSecurityParms->CSPUserIdPtr,pSecurityParms->CSPUserIdLength);
spuser[pSecurityParms->CSPUserIdLength]=0;
sppass = malloc(pSecurityParms->CSPPasswordLength+1);
strncpy(sppass,pSecurityParms->CSPPasswordPtr,pSecurityParms->CSPPasswordLength);
sppass[pSecurityParms->CSPPasswordLength]=0;
gorc = Authenticate(spuser,sppass);
if (gorc == MQRC_NONE) {
*pCompCode = MQCC_OK;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
memcpy( pIdentityContext->UserIdentifier
, spuser
, sizeof(pIdentityContext->UserIdentifier) );
} else {
*pCompCode = MQCC_WARNING;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
//we print to error file only if error'd
PrintDateTime();
if (fp) {
fprintf(fp, authuserfmt,
pIdentityContext->UserIdentifier,
pApplicationContext->EffectiveUserID,
pApplicationContext->ApplName,
pIdentityContext->ApplIdentityData,
OAEnvStr(pApplicationContext->Environment),
pApplicationContext->ProcessId,
pApplicationContext->ThreadId);
fprintf(fp,"\tCSP UserId : %s\n", spuser);
fprintf(fp,"\tCSP Password : %s\n", "****..");
fprintf(fp,"\tPAS-Compcode:%d\n",*pCompCode);
fprintf(fp,"\tPAS-Reasoncode:%d\n",*pReason);
}
}
free(spuser);
free(sppass);
} else {
//this is only a normal UID authentication.
spuser = malloc(sizeof(PMQCHAR12));
strncpy(spuser,pApplicationContext->EffectiveUserID,strlen(pApplicationContext->EffectiveUserID));
spuser[sizeof(PMQCHAR12)]=0;
gorc = CheckAuthority(spuser);
if (gorc == MQRC_NONE){
*pCompCode = MQCC_OK;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
memcpy( pIdentityContext->UserIdentifier
, spuser
, sizeof(pIdentityContext->UserIdentifier) );
} else {
*pCompCode = MQCC_WARNING;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
//we print only if error'd
PrintDateTime();
if (fp)
{
fprintf(fp, authuserfmt,
pIdentityContext->UserIdentifier,
pApplicationContext->EffectiveUserID,
pApplicationContext->ApplName,
pIdentityContext->ApplIdentityData,
OAEnvStr(pApplicationContext->Environment),
pApplicationContext->ProcessId,
pApplicationContext->ThreadId
);
fprintf(fp,"\tUID : %s\n", spuser);
fprintf(fp,"\tPAS-Compcode:%d\n",*pCompCode);
fprintf(fp,"\tPAS-Reasoncode:%d\n",*pReason);
}
}
}
return;
}
static void MQENTRY OAFreeUser (
PMQCHAR pQMgrName,
PMQZFP pFreeParms,
PMQBYTE pComponentData,
PMQLONG pContinuation,
PMQLONG pCompCode,
PMQLONG pReason)
{
*pCompCode = MQCC_WARNING;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
return;
}
static void MQENTRY OATermAuth(
MQHCONFIG hc,
MQLONG Options,
PMQCHAR pQMgrName,
PMQBYTE pComponentData,
PMQLONG pCompCode,
PMQLONG pReason)
{
if ((primary_process) && ((Options & MQZTO_PRIMARY) == MQZTO_PRIMARY) ||
((Options & MQZTO_SECONDARY) == MQZTO_SECONDARY))
{
if (fp)
{
fclose(fp);
fp = NULL;
}
}
*pCompCode = MQCC_OK;
*pReason = MQRC_NONE;
}
static void PrintDateTime() {
FindSize();
struct tm *local;
time_t t;
t = time(NULL);
local = localtime(&t);
if (fp) {
fprintf(fp, "-------------------------------------------------\n");
fprintf(fp, "Local time: %s", asctime(local));
local = gmtime(&t);
fprintf(fp, "UTC time: %s", asctime(local));
}
return;
}
static char *OAEnvStr(MQLONG x)
{
switch (x)
{
case MQXE_OTHER: return "Application";
case MQXE_MCA: return "Channel";
case MQXE_MCA_SVRCONN: return "Channel SvrConn";
case MQXE_COMMAND_SERVER: return "Command Server";
case MQXE_MQSC: return "MQSC";
default: return "Invalid Environment";
}
}
static void FindSize()
{
int sz = 0;
int prev=ftell(fp);
fseek(fp, 0L, SEEK_END);
sz=ftell(fp);
//if log file size goes over 1mb, rewind it.
if (sz > 1000000) {
rewind(fp);
} else {
fseek(fp, prev, SEEK_SET);
}
}
*/
import "C"
import "github.com/ibm-messaging/mq-container/internal/htpasswd"
//export MQStart
func MQStart(hc C.MQHCONFIG, Options C.MQLONG, QMgrName C.PMQCHAR, ComponentDataLength C.MQLONG, ComponentData C.PMQBYTE, Version C.PMQLONG, pCompCode C.PMQLONG, pReason C.PMQLONG) {
C.PASStart(hc, Options, QMgrName, ComponentDataLength, ComponentData, Version, pCompCode, pReason)
}
//export Authenticate
func Authenticate(x *C.char, y *C.char) C.int {
user := C.GoString(x)
pwd := C.GoString(y)
found, ok, err := htpasswd.AuthenticateUser(user, pwd, false)
if !found || !ok || err != nil {
return C.MQRC_UNKNOWN_OBJECT_NAME
}
return C.MQRC_NONE
}
//export CheckAuthority
func CheckAuthority(x *C.char) C.int {
user := C.GoString(x)
found, err := htpasswd.ValidateUser(user, false)
if !found || err != nil {
return C.MQRC_UNKNOWN_OBJECT_NAME
}
return C.MQRC_NONE
}
func main() {}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,9 @@ package ready
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
) )
const fileName string = "/run/runmqserver/ready" const fileName string = "/run/runmqserver/ready"
@@ -62,3 +65,24 @@ func Check() (bool, error) {
} }
return exists, nil return exists, nil
} }
// IsRunningAsActiveQM returns true if the queue manager is running in active mode
func IsRunningAsActiveQM(name string) (bool, error) {
return isRunningQM(name, "(RUNNING)")
}
// IsRunningAsStandbyQM returns true if the queue manager is running in standby mode
func IsRunningAsStandbyQM(name string) (bool, error) {
return isRunningQM(name, "(RUNNING AS STANDBY)")
}
func isRunningQM(name string, status string) (bool, error) {
out, _, err := command.Run("dspmq", "-n", "-m", name)
if err != nil {
return false, err
}
if strings.Contains(string(out), status) {
return true, nil
}
return false, nil
}

576
internal/tls/tls.go Normal file
View File

@@ -0,0 +1,576 @@
/*
© Copyright IBM Corporation 2019
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tls
import (
"bufio"
"fmt"
"io/ioutil"
pwr "math/rand"
"os"
"path/filepath"
"strings"
"time"
"crypto/rand"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
pkcs "software.sslmate.com/src/go-pkcs12"
"github.com/ibm-messaging/mq-container/internal/keystore"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
"github.com/ibm-messaging/mq-container/pkg/logger"
)
// cmsKeystoreName is the name of the CMS Keystore
const cmsKeystoreName = "key.kdb"
// p12TruststoreName is the name of the PKCS#12 Truststore
const p12TruststoreName = "trust.p12"
// keystoreDir is the location for the CMS Keystore & PKCS#12 Truststore
const keystoreDir = "/run/runmqserver/tls/"
// keyDir is the location of the keys to import
const keyDir = "/etc/mqm/pki/keys"
// trustDir is the location of the trust certificates to import
const trustDir = "/etc/mqm/pki/trust"
type KeyStoreData struct {
Keystore *keystore.KeyStore
Password string
TrustedCerts []*pem.Block
KnownFingerPrints []string
KeyLabels []string
}
type P12KeyFiles struct {
Keystores []string
Password string
}
// ConfigureTLSKeystores configures the CMS Keystore & PKCS#12 Truststore
func ConfigureTLSKeystores() (string, KeyStoreData, KeyStoreData, error) {
// Create the CMS Keystore & PKCS#12 Truststore
cmsKeystore, p12Truststore, err := generateAllKeystores()
if err != nil {
return "", cmsKeystore, p12Truststore, err
}
// Process all keys - add them to the CMS KeyStore
keyLabel, err := processKeys(&cmsKeystore, &p12Truststore)
if err != nil {
return "", cmsKeystore, p12Truststore, err
}
// Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore
err = processTrustCertificates(&cmsKeystore, &p12Truststore)
if err != nil {
return "", cmsKeystore, p12Truststore, err
}
return keyLabel, cmsKeystore, p12Truststore, err
}
// ConfigureTLS configures TLS for the queue manager
func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log *logger.Logger) error {
const mqsc string = "/etc/mqm/15-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
"CertificateLabel": keyLabel,
}, log)
if err != nil {
return err
}
if devMode && keyLabel != "" {
err = configureTLSDev(log)
if err != nil {
return err
}
}
return nil
}
// configureTLSDev configures TLS for the developer defaults
func configureTLSDev(log *logger.Logger) error {
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
if os.Getenv("MQ_DEV") == "true" {
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{}, log)
if err != nil {
return err
}
} else {
_, err := os.Stat(mqsc)
if !os.IsNotExist(err) {
err = os.Remove(mqsc)
if err != nil {
return fmt.Errorf("Failed to remove file %s: %v", mqsc, err)
}
}
}
return nil
}
// generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore
func generateAllKeystores() (KeyStoreData, KeyStoreData, error) {
var cmsKeystore, p12Truststore KeyStoreData
// Generate a pasword for use with both the CMS Keystore & PKCS#12 Truststore
pw := generateRandomPassword()
cmsKeystore.Password = pw
p12Truststore.Password = pw
// Create the Keystore directory - if it does not already exist
// #nosec G301 - write group permissions are required
err := os.MkdirAll(keystoreDir, 0770)
if err != nil {
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create Keystore directory: %v", err)
}
// Create the CMS Keystore
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password)
err = cmsKeystore.Keystore.Create()
if err != nil {
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create CMS Keystore: %v", err)
}
// Create the PKCS#12 Truststore
p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password)
err = p12Truststore.Keystore.Create()
if err != nil {
return cmsKeystore, p12Truststore, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err)
}
return cmsKeystore, p12Truststore, nil
}
// processKeys processes all keys - adding them to the CMS KeyStore
func processKeys(cmsKeystore, p12Truststore *KeyStoreData) (string, error) {
// Key label - will be set to the label of the first set of keys
keyLabel := ""
// Process all keys
keyList, err := ioutil.ReadDir(keyDir)
if err == nil && len(keyList) > 0 {
// Process each set of keys - each set should contain files: *.key & *.crt
for _, keySet := range keyList {
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, keySet.Name()))
// Ensure the label of the set of keys does not match the name of the PKCS#12 Truststore
if keySet.Name() == p12TruststoreName[0:len(p12TruststoreName)-len(filepath.Ext(p12TruststoreName))] {
return "", fmt.Errorf("Key label cannot be set to the Truststore name: %v", keySet.Name())
}
// Process private key (*.key)
privateKey, keyPrefix, err := processPrivateKey(keySet.Name(), keys)
if err != nil {
return "", err
}
// If private key does not exist - skip this set of keys
if privateKey == nil {
continue
}
// Process certificates (*.crt) - public certificate & optional CA certificate
publicCertificate, caCertificate, err := processCertificates(keySet.Name(), keyPrefix, keys, cmsKeystore, p12Truststore)
if err != nil {
return "", err
}
// Create a new PKCS#12 Keystore - containing private key, public certificate & optional CA certificate
file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, cmsKeystore.Password)
if err != nil {
return "", fmt.Errorf("Failed to encode PKCS#12 Keystore %s: %v", keySet.Name()+".p12", err)
}
err = ioutil.WriteFile(filepath.Join(keystoreDir, keySet.Name()+".p12"), file, 0644)
if err != nil {
return "", fmt.Errorf("Failed to write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
}
// Import the new PKCS#12 Keystore into the CMS Keystore
err = cmsKeystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), cmsKeystore.Password)
if err != nil {
return "", fmt.Errorf("Failed tp import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
}
// Relabel the certificate in the CMS Keystore
err = relabelCertificate(keySet.Name(), cmsKeystore)
if err != nil {
return "", err
}
// Set key label - for first set of keys only
if keyLabel == "" {
keyLabel = keySet.Name()
}
}
}
return keyLabel, nil
}
// processTrustCertificates processes all trust certificates - adding them to the CMS KeyStore & PKCS#12 Truststore
func processTrustCertificates(cmsKeystore, p12Truststore *KeyStoreData) error {
// Process all trust certiifcates
trustList, err := ioutil.ReadDir(trustDir)
if err == nil && len(trustList) > 0 {
// Process each set of keys
for _, trustSet := range trustList {
keys, _ := ioutil.ReadDir(filepath.Join(trustDir, trustSet.Name()))
for _, key := range keys {
if strings.HasSuffix(key.Name(), ".crt") {
// #nosec G304 - filename variable is derived from contents of 'trustDir' which is a defined constant
file, err := ioutil.ReadFile(filepath.Join(trustDir, trustSet.Name(), key.Name()))
if err != nil {
return fmt.Errorf("Failed to read file %s: %v", filepath.Join(trustDir, trustSet.Name(), key.Name()), err)
}
for string(file) != "" {
var block *pem.Block
block, file = pem.Decode(file)
if block == nil {
break
}
// Add to known certificates for the CMS Keystore
err = addToKnownCertificates(block, cmsKeystore, true)
if err != nil {
return fmt.Errorf("Failed to add to know certificates for CMS Keystore")
}
// Add to known certificates for the PKCS#12 Truststore
err = addToKnownCertificates(block, p12Truststore, true)
if err != nil {
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
}
}
}
}
}
}
// Add all trust certificates to PKCS#12 Truststore
if len(p12Truststore.TrustedCerts) > 0 {
err = addCertificatesToTruststore(p12Truststore)
if err != nil {
return err
}
}
// Add all trust certificates to CMS Keystore
if len(cmsKeystore.TrustedCerts) > 0 {
err = addCertificatesToCMSKeystore(cmsKeystore)
if err != nil {
return err
}
}
return nil
}
// processPrivateKey processes the private key (*.key) from a set of keys
func processPrivateKey(keySetName string, keys []os.FileInfo) (interface{}, string, error) {
var privateKey interface{}
keyPrefix := ""
for _, key := range keys {
if strings.HasSuffix(key.Name(), ".key") {
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
if err != nil {
return nil, "", fmt.Errorf("Failed to read private key %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
block, _ := pem.Decode(file)
if block == nil {
return nil, "", fmt.Errorf("Failed to decode private key %s: pem.Decode returned nil", filepath.Join(keyDir, keySetName, key.Name()))
}
// Check if the private key is PKCS1
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
// Check if the private key is PKCS8
privateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, "", fmt.Errorf("Failed to parse private key %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
}
keyPrefix = key.Name()[0 : len(key.Name())-len(filepath.Ext(key.Name()))]
}
}
return privateKey, keyPrefix, nil
}
// processCertificates processes the certificates (*.crt) from a set of keys
func processCertificates(keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) {
var publicCertificate *x509.Certificate
var caCertificate []*x509.Certificate
for _, key := range keys {
if strings.HasPrefix(key.Name(), keyPrefix) && strings.HasSuffix(key.Name(), ".crt") {
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
if err != nil {
return nil, nil, fmt.Errorf("Failed to read public certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
block, _ := pem.Decode(file)
if block == nil {
return nil, nil, fmt.Errorf("Failed to decode public certificate %s: pem.Decode returned nil", filepath.Join(keyDir, keySetName, key.Name()))
}
publicCertificate, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf("Failed to parse public certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
// Add to known certificates for the CMS Keystore
err = addToKnownCertificates(block, cmsKeystore, false)
if err != nil {
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
}
} else if strings.HasSuffix(key.Name(), ".crt") {
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
if err != nil {
return nil, nil, fmt.Errorf("Failed to read CA certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
for string(file) != "" {
var block *pem.Block
block, file = pem.Decode(file)
if block == nil {
break
}
// Add to known certificates for the CMS Keystore
err = addToKnownCertificates(block, cmsKeystore, false)
if err != nil {
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
}
// Add to known certificates for the PKCS#12 Truststore
err = addToKnownCertificates(block, p12Truststore, true)
if err != nil {
return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
}
certificate, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf("Failed to parse CA certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
}
caCertificate = append(caCertificate, certificate)
}
}
}
return publicCertificate, caCertificate, nil
}
// relabelCertificate sets a new label for a certificate in the CMS Keystore
func relabelCertificate(newLabel string, cmsKeystore *KeyStoreData) error {
allLabels, err := cmsKeystore.Keystore.GetCertificateLabels()
if err != nil {
return fmt.Errorf("Failed to get list of all certificate labels from CMS Keystore: %v", err)
}
relabelled := false
for _, label := range allLabels {
found := false
for _, keyLabel := range cmsKeystore.KeyLabels {
if strings.Trim(label, "\"") == keyLabel {
found = true
break
}
}
if !found {
err = cmsKeystore.Keystore.RenameCertificate(strings.Trim(label, "\""), newLabel)
if err != nil {
return err
}
relabelled = true
cmsKeystore.KeyLabels = append(cmsKeystore.KeyLabels, newLabel)
break
}
}
if !relabelled {
return fmt.Errorf("Failed to relabel certificate for %s in CMS keystore", newLabel)
}
return nil
}
// addCertificatesToTruststore adds trust certificates to the PKCS#12 Truststore
func addCertificatesToTruststore(p12Truststore *KeyStoreData) error {
temporaryPemFile := filepath.Join("/tmp", "trust.pem")
_, err := os.Stat(temporaryPemFile)
if err == nil {
err = os.Remove(temporaryPemFile)
if err != nil {
return fmt.Errorf("Failed to remove file %v: %v", temporaryPemFile, err)
}
}
err = writeCertificatesToFile(temporaryPemFile, p12Truststore.TrustedCerts)
if err != nil {
return err
}
err = p12Truststore.Keystore.AddNoLabel(temporaryPemFile)
if err != nil {
return fmt.Errorf("Failed to add certificates to PKCS#12 Truststore: %v", err)
}
// Relabel all certiifcates
allCertificates, err := p12Truststore.Keystore.ListAllCertificates()
if err != nil || len(allCertificates) <= 0 {
return fmt.Errorf("Failed to get any certificates from PKCS#12 Truststore: %v", err)
}
for i, certificate := range allCertificates {
certificate = strings.Trim(certificate, "\"")
certificate = strings.TrimSpace(certificate)
newLabel := fmt.Sprintf("Trust%d", i)
err = p12Truststore.Keystore.RenameCertificate(certificate, newLabel)
if err != nil || len(allCertificates) <= 0 {
return fmt.Errorf("Failed to rename certificate %s to %s in PKCS#12 Truststore: %v", certificate, newLabel, err)
}
}
return nil
}
// addCertificatesToCMSKeystore adds trust certificates to the CMS keystore
func addCertificatesToCMSKeystore(cmsKeystore *KeyStoreData) error {
temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem")
_, err := os.Stat(temporaryPemFile)
if err == nil {
err = os.Remove(temporaryPemFile)
if err != nil {
return fmt.Errorf("Failed to remove file %v: %v", temporaryPemFile, err)
}
}
err = writeCertificatesToFile(temporaryPemFile, cmsKeystore.TrustedCerts)
if err != nil {
return err
}
err = cmsKeystore.Keystore.AddNoLabel(temporaryPemFile)
if err != nil {
return fmt.Errorf("Failed to add certificates to CMS keystore: %v", err)
}
return nil
}
// generateRandomPassword generates a random 12 character password from the characters a-z, A-Z, 0-9
func generateRandomPassword() string {
pwr.Seed(time.Now().Unix())
validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
validcharArray := []byte(validChars)
password := ""
for i := 0; i < 12; i++ {
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
}
return password
}
// addToKnownCertificates adds to the list of known certificates for a Keystore
func addToKnownCertificates(block *pem.Block, keyData *KeyStoreData, addToKeystore bool) error {
sha512str, err := getCertificateFingerprint(block)
if err != nil {
return err
}
known := false
for _, fingerprint := range keyData.KnownFingerPrints {
if fingerprint == sha512str {
known = true
break
}
}
if !known {
if addToKeystore {
keyData.TrustedCerts = append(keyData.TrustedCerts, block)
}
keyData.KnownFingerPrints = append(keyData.KnownFingerPrints, sha512str)
}
return nil
}
// getCertificateFingerprint returns a fingerprint for a certificate
func getCertificateFingerprint(block *pem.Block) (string, error) {
certificate, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("Failed to parse x509 certificate: %v", err)
}
sha512Sum := sha512.Sum512(certificate.Raw)
sha512str := string(sha512Sum[:])
return sha512str, nil
}
// writeCertificatesToFile writes a list of certificates to a file
func writeCertificatesToFile(file string, certificates []*pem.Block) error {
f, err := os.Create(file)
if err != nil {
return fmt.Errorf("Failed to create file %s: %v", file, err)
}
defer f.Close()
w := bufio.NewWriter(f)
for i, c := range certificates {
err := pem.Encode(w, c)
if err != nil {
return fmt.Errorf("Failed to encode certificate number %d: %v", i, err)
}
err = w.Flush()
if err != nil {
return fmt.Errorf("Failed to write certificate to file %s: %v", file, err)
}
}
return nil
}

97
internal/tls/tls_web.go Normal file
View File

@@ -0,0 +1,97 @@
/*
© Copyright IBM Corporation 2019, 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tls
import (
"fmt"
"os"
"path/filepath"
"github.com/ibm-messaging/mq-container/internal/keystore"
)
// webKeystoreDefault is the name of the default web server Keystore
const webKeystoreDefault = "default.p12"
// ConfigureWebTLS configures TLS for the web server
func ConfigureWebTLS(keyLabel string) error {
// Return immediately if we have no certificate to use as identity
if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") == "" {
return nil
}
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
tls := "tls.xml"
tlsConfig := filepath.Join(webConfigDir, tls)
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
err := os.Remove(tlsConfig)
if err != nil {
return fmt.Errorf("Failed to delete file %s: %v", tlsConfig, err)
}
// Symlink here to prevent issues on restart
err = os.Symlink(newTLSConfig, tlsConfig)
if err != nil {
return fmt.Errorf("Failed to create symlink %s->%s: %v", newTLSConfig, tlsConfig, err)
}
return nil
}
// ConfigureWebKeyStore configures the Web Keystore
func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (string, error) {
if webKeystore == "" {
webKeystore = webKeystoreDefault
}
webKeystoreFile := filepath.Join(keystoreDir, webKeystore)
// Check if a new self-signed certificate should be generated
genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME")
if genHostName != "" {
// Create the Web Keystore
newWebKeystore := keystore.NewPKCS12KeyStore(webKeystoreFile, p12Truststore.Password)
err := newWebKeystore.Create()
if err != nil {
return "", fmt.Errorf("Failed to create Web Keystore %s: %v", webKeystoreFile, err)
}
// Generate a new self-signed certificate in the Web Keystore
err = newWebKeystore.CreateSelfSignedCertificate("default", fmt.Sprintf("CN=%s", genHostName), genHostName)
if err != nil {
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err)
}
} else {
// Check Web Keystore already exists
_, err := os.Stat(webKeystoreFile)
if err != nil {
return "", fmt.Errorf("Failed to find existing Web Keystore %s: %v", webKeystoreFile, err)
}
}
// Check Web Truststore already exists
_, err := os.Stat(p12Truststore.Keystore.Filename)
if err != nil {
return "", fmt.Errorf("Failed to find existing Web Truststore %s: %v", p12Truststore.Keystore.Filename, err)
}
return webKeystore, nil
}

41
internal/user/user.go Normal file
View File

@@ -0,0 +1,41 @@
/*
© Copyright IBM Corporation 2018, 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"golang.org/x/sys/unix"
)
// User holds information on primary and supplemental OS groups
type User struct {
UID int
PrimaryGID int
SupplementalGID []int
}
// GetUser returns the current user and group information
func GetUser() (User, error) {
u := User{
UID: unix.Geteuid(),
PrimaryGID: unix.Getgid(),
}
groups, err := unix.Getgroups()
if err != nil {
return u, err
}
u.SupplementalGID = groups
return u, nil
}

View File

@@ -1,7 +1,7 @@
Fat manifests Fat manifests
============= =============
These are the fat manifests used by Docker Hub and Docker store to handle images with multiple CPU architectures. These are the fat manifests used by Docker Hub to handle images with multiple CPU architectures.
They are used in conjunction with [manifest-tool](https://github.com/estesp/manifest-tool), for example: They are used in conjunction with [manifest-tool](https://github.com/estesp/manifest-tool), for example:

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,18 +12,17 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
image: ibmcorp/mqadvanced-server-dev:9.1.0.0 image: ibmcom/mq:9.1.2.0-UBI
manifests: manifests:
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-x86_64 - image: ibmcom/mq:9.1.2.0-UBI-amd64
platform: platform:
architecture: amd64 architecture: amd64
os: linux os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-ppc64le - image: ibmcom/mq:9.1.2.0-UBI-ppc64le
platform: platform:
architecture: ppc64le architecture: ppc64le
os: linux os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-s390x - image: ibmcom/mq:9.1.2.0-UBI-s390x
platform: platform:
architecture: s390x architecture: s390x
os: linux os: linux

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2018, 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,17 +12,17 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
image: ibmcom/mq:9 image: ibmcom/mq:9.1.2.0
manifests: manifests:
- image: ibmcom/mq:9.1.1.0-x86_64 - image: ibmcom/mq:9.1.2.0-x86_64
platform: platform:
architecture: amd64 architecture: amd64
os: linux os: linux
- image: ibmcom/mq:9.1.1.0-ppc64le - image: ibmcom/mq:9.1.2.0-ppc64le
platform: platform:
architecture: ppc64le architecture: ppc64le
os: linux os: linux
- image: ibmcom/mq:9.1.1.0-s390x - image: ibmcom/mq:9.1.2.0-s390x
platform: platform:
architecture: s390x architecture: s390x
os: linux os: linux

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2019
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -12,18 +12,17 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
image: ibmcorp/mqadvanced-server-dev:9.1.1.0 image: ibmcom/mq:9.1.3.0-r3
manifests: manifests:
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-x86_64 - image: ibmcom/mq:9.1.3.0-r3-amd64
platform: platform:
architecture: amd64 architecture: amd64
os: linux os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-ppc64le - image: ibmcom/mq:9.1.3.0-r3-ppc64le
platform: platform:
architecture: ppc64le architecture: ppc64le
os: linux os: linux
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-s390x - image: ibmcom/mq:9.1.3.0-r3-s390x
platform: platform:
architecture: s390x architecture: s390x
os: linux os: linux

View File

@@ -0,0 +1,28 @@
# © Copyright IBM Corporation 2019
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
image: ibmcom/mq:9.1.4.0-r1
manifests:
- image: ibmcom/mq:9.1.4.0-r1-amd64
platform:
architecture: amd64
os: linux
- image: ibmcom/mq:9.1.4.0-r1-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcom/mq:9.1.4.0-r1-s390x
platform:
architecture: s390x
os: linux

View File

@@ -0,0 +1,28 @@
# © Copyright IBM Corporation 2020
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
image: ibmcom/mq:9.1.5.0-r1
manifests:
- image: ibmcom/mq:9.1.5.0-r1-amd64
platform:
architecture: amd64
os: linux
- image: ibmcom/mq:9.1.5.0-r1-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcom/mq:9.1.5.0-r1-s390x
platform:
architecture: s390x
os: linux

View File

@@ -0,0 +1,20 @@
# © Copyright IBM Corporation 2018, 2020
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
image: ibmcom/mq:9.2.0.0-r1
manifests:
- image: ibmcom/mq:9.2.0.0-r1-amd64
platform:
architecture: amd64
os: linux

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018 # © Copyright IBM Corporation 2018, 2020
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
image: ibmcom/mq:latest image: ibmcom/mq:latest
manifests: manifests:
- image: ibmcom/mq:9.1.1.0-x86_64 - image: ibmcom/mq:9.2.0.0-r1-amd64
platform: platform:
architecture: amd64 architecture: amd64
os: linux os: linux
- image: ibmcom/mq:9.1.1.0-ppc64le
platform:
architecture: ppc64le
os: linux
- image: ibmcom/mq:9.1.1.0-s390x
platform:
architecture: s390x
os: linux

View File

@@ -1,7 +0,0 @@
This is a work-in-progress for a Docker image based on Red Hat Enterprise Linux (RHEL).
The current MQ container build requires Docker V17.05 or greater (required features include multi-stage Docker build, and "ARG"s in the "FROM" statement). Red Hat Enterprise Linux V7.5 includes Docker up to version V1.13.
In order to build images with Red Hat Enterprise Linux, license registration is required. The license of the host server can be used, as long as you either use Red Hat's patched version of Docker (which is an old version), or if you use alternative container management tools such as [`buildah`](https://github.com/projectatomic/buildah/) and `podman` (from [`libpod`](https://github.com/projectatomic/libpod)).
This directory contains scripts for building with `buildah`. The build itself isn't containerized, so more software than usual is needed on the RHEL host, so an Ansible playbook is also provided to help set up the host.

View File

@@ -1,47 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Builds and tests the golang programs used by the MQ image.
set -e
cd $GOPATH/src/github.com/ibm-messaging/mq-container/
# Build and test the Go code
mkdir -p build
cd build
rm -f chkmqready chkmqhealthy runmqserver runmqdevserver
if [ "$MQDEV" = "TRUE" ]; then
# Build and test the Go code
go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" --tags 'mqdev' ../cmd/runmqserver/
go build ../cmd/runmqdevserver/
else
go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\"" ../cmd/runmqserver/
fi
go build ../cmd/chkmqready/
go build ../cmd/chkmqhealthy/
go test -v ../cmd/runmqserver/
go test -v ../cmd/chkmqready/
go test -v ../cmd/chkmqhealthy/
if [ "$MQDEV" = "TRUE" ]; then
go test -v ../cmd/runmqdevserver
fi
go test -v ../internal/...
go vet ../cmd/... ../internal/...

View File

@@ -1,45 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Run the Go build script inside the Go container, mounting the source
# directory in
function usage {
echo "Usage: $0 TAG DevModeFlag"
exit 20
}
if [ "$#" -ne 2 ]; then
echo "ERROR: Invalid number of parameters"
usage
fi
readonly tag=$1
readonly dev=$2
IMAGE_REVISION=${IMAGE_REVISION:="Not Applicable"}
IMAGE_SOURCE=${IMAGE_SOURCE:="Not Applicable"}
podman run \
--volume ${PWD}:/go/src/github.com/ibm-messaging/mq-container/ \
--env GOPATH=/go \
--env IMAGE_REVISION="$IMAGE_REVISION" \
--env IMAGE_SOURCE="$IMAGE_SOURCE" \
--env MQDEV=${dev} \
--rm \
${tag} \
bash -c "cd /go/src/github.com/ibm-messaging/mq-container/ && ./mq-advanced-server-rhel/go-build.sh"

View File

@@ -1,81 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Install one or more MQ components into a buildah container
set -ex
function usage {
echo "Usage: $0 MQContainer MountLocation ARCHIVENAME PACKAGES"
exit 20
}
if [ "$#" -ne 4 ]; then
echo "ERROR: Invalid number of parameters"
usage
fi
readonly ctr_mq=$1
readonly mnt_mq=$2
readonly archive=$3
readonly mq_packages=$4
readonly dir_extract=/tmp/extract
if [ ! -d ${dir_extract}/MQServer ]; then
mkdir -p ${dir_extract}
echo Extracting $archive
tar -zxf $archive -C ${dir_extract}
echo Extracting finished
fi
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
# Accept the MQ license
buildah run --volume ${dir_extract}:/mnt/mq-download $ctr_mq -- /mnt/mq-download/MQServer/mqlicense.sh -text_only -accept
buildah run --volume ${dir_extract}:/mnt/mq-download $ctr_mq -- bash -c "cd /mnt/mq-download/MQServer && rpm -ivh $mq_packages"
rm -rf ${dir_extract}/MQServer
# Remove 32-bit libraries from 64-bit container
find $mnt_mq/opt/mqm $mnt_mq/var/mqm -type f -exec file {} \; | awk -F: '/ELF 32-bit/{print $1}' | xargs --no-run-if-empty rm -f
# Remove tar.gz files unpacked by RPM postinst scripts
find $mnt_mq/opt/mqm -name '*.tar.gz' -delete
# Recommended: Set the default MQ installation (makes the MQ commands available on the PATH)
buildah run $ctr_mq -- /opt/mqm/bin/setmqinst -p /opt/mqm -i
mkdir -p $mnt_mq/run/runmqserver
chown 888:888 $mnt_mq/run/runmqserver
# Remove the directory structure under /var/mqm which was created by the installer
rm -rf $mnt_mq/var/mqm
# Create the mount point for volumes
mkdir -p $mnt_mq/mnt/mqm
# Create a symlink for /var/mqm -> /mnt/mqm/data
buildah run $ctr_mq -- ln -s /mnt/mqm/data /var/mqm
# Optional: Set these values for the IBM Cloud Vulnerability Report
sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' $mnt_mq/etc/login.defs
sed -i 's/PASS_MIN_DAYS\t0/PASS_MIN_DAYS\t1/' $mnt_mq/etc/login.defs
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $mnt_mq/etc/pam.d/password-auth
buildah run $ctr_mq -- cp -rs /opt/mqm/licenses/ /

View File

@@ -1,141 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Build a RHEL image, using the buildah tool
# Usage
# mq-buildah.sh ARCHIVEFILE PACKAGES
set -x
set -e
function usage {
echo "Usage: $0 ARCHIVENAME PACKAGES TAG VERSION MQDevFlag"
exit 20
}
if [ "$#" -ne 5 ]; then
echo "ERROR: Invalid number of parameters"
usage
fi
###############################################################################
# Setup MQ server working container
###############################################################################
readonly ctr_mq=$(buildah from rhel7)
if [ -z "$ctr_mq" ]
then
echo "ERROR: ctr_mq is empty. Check above output for errors"
exit 50
fi
readonly mnt_mq=$(buildah mount $ctr_mq)
if [ -z "$mnt_mq" ]
then
echo "ERROR: mnt_mq is empty. Check above output for errors"
exit 50
fi
readonly archive=downloads/$1
readonly packages=$2
readonly tag=$3
readonly version=$4
readonly mqdev=$5
###############################################################################
# Install MQ server
###############################################################################
groupadd --root ${mnt_mq} --system --gid 888 mqm
useradd --root ${mnt_mq} --system --uid 888 --gid mqm mqm
usermod --root ${mnt_mq} -aG root mqm
usermod --root ${mnt_mq} -aG mqm root
# Install the packages required by MQ
buildah run $ctr_mq -- yum install -y --setopt install_weak_deps=false --setopt=tsflags=nodocs --setopt=override_install_langs=en_US.utf8 \
bash \
bc \
coreutils \
file \
findutils \
gawk \
glibc-common \
grep \
passwd \
procps-ng \
sed \
tar \
util-linux
# Clean up cached files
buildah run $ctr_mq -- yum clean all
rm -rf ${mnt_mq}/var/cache/yum/*
# Install MQ server packages into the MQ builder image
./mq-advanced-server-rhel/install-mq-rhel.sh ${ctr_mq} "${mnt_mq}" "${archive}" "${packages}"
# Create the directory for MQ configuration files
mkdir -p ${mnt_mq}/etc/mqm
chown 888:888 ${mnt_mq}/etc/mqm
# Install the Go binaries into the image
install --mode 0750 --owner 888 --group 888 ./build/runmqserver ${mnt_mq}/usr/local/bin/
install --mode 6750 --owner 888 --group 888 ./build/chk* ${mnt_mq}/usr/local/bin/
install --mode 0750 --owner 888 --group 888 ./NOTICES.txt ${mnt_mq}/opt/mqm/licenses/notices-container.txt
###############################################################################
# Final Buildah commands
###############################################################################
if [ "$mqdev" = "TRUE" ]; then
OSTAG="mq messaging developer"
DISNAME="IBM MQ Advanced Server Developer Edition"
PID="98102d16795c4263ad9ca075190a2d4d"
else
OSTAG="mq messaging"
DISNAME="IBM MQ Advanced Server"
PID="4486e8c4cc9146fd9b3ce1f14a2dfc5b"
fi
buildah config \
--port 1414/tcp \
--port 9157/tcp \
--os linux \
--label architecture=x86_64 \
--label io.openshift.tags="$OSTAG" \
--label io.k8s.display-name="$DISNAME" \
--label io.k8s.description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
--label name="${tag%:*}" \
--label vendor="IBM" \
--label version="$version" \
--label release="1" \
--label run="docker run -d -e LICENSE=accept --name ibm-mq ${tag%:*}" \
--label summary="$DISNAME" \
--label description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
--label IBM_PRODUCT_ID="$PID" \
--label IBM_PRODUCT_NAME="$DISNAME" \
--label IBM_PRODUCT_VERSION="$version" \
--env AMQ_ADDITIONAL_JSON_LOG=1 \
--env LANG=en_US.UTF-8 \
--env LOG_FORMAT=basic \
--entrypoint runmqserver \
--user root \
$ctr_mq
buildah unmount $ctr_mq
buildah commit $ctr_mq $tag
buildah rm $ctr_mq

View File

@@ -1,76 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Build a RHEL image for building Go programs which use MQ
set -ex
function usage {
echo "Usage: $0 ARCHIVENAME TAG"
exit 20
}
if [ "$#" -ne 2 ]; then
echo "ERROR: Invalid number of parameters"
usage
fi
readonly mq_archive=downloads/$1
readonly tag=$2
# Use plain RHEL 7 container
# Note: Red Hat's devtools/go-toolset-7-rhel7 image doesn't allow use of 'root'
# user required for installing the MQ SDK
readonly ctr_mq=$(buildah from rhel7)
if [ -z "$ctr_mq" ]
then
echo "ERROR: ctr_mq is empty. Check above output for errors"
exit 50
fi
readonly mnt_mq=$(buildah mount $ctr_mq)
if [ -z "$mnt_mq" ]
then
echo "ERROR: mnt_mq is empty. Check above output for errors"
exit 50
fi
# Add mqm user
sudo groupadd --root $mnt_mq --system --gid 888 mqm
sudo useradd --root $mnt_mq --system --uid 888 --gid mqm mqm
sudo usermod --root $mnt_mq -aG root mqm
sudo usermod --root $mnt_mq -aG mqm root
# Enable Yum repository for "optional" RPMs, which is needed for "golang"
buildah run ${ctr_mq} -- yum-config-manager --enable rhel-7-server-optional-rpms
# Install Go compiler
buildah run ${ctr_mq} -- yum install -y golang git gcc
# Install the MQ SDK into the Go builder image
./mq-advanced-server-rhel/install-mq-rhel.sh ${ctr_mq} "${mnt_mq}" "${mq_archive}" "MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm"
# Clean up Yum files
buildah run ${ctr_mq} -- yum clean all --releasever 7
rm -rf ${mnt_mq}/var/cache/yum/*
buildah unmount ${ctr_mq}
# Set environment variables for MQ/Go compilation
buildah config \
--os linux \
--env CGO_CFLAGS="-I/opt/mqm/inc/" \
--env CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
${ctr_mq}
buildah commit ${ctr_mq} ${tag}
buildah rm ${ctr_mq}

View File

@@ -1,113 +0,0 @@
#!/bin/bash
# -*- mode: sh -*-
# © Copyright IBM Corporation 2018
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Build a RHEL image, using the buildah tool
# Usage
# mq-buildah.sh ARCHIVEFILE PACKAGES
set -x
set -e
function usage {
echo "Usage: $0 BASETAG TAG VERSION"
exit 20
}
if [ "$#" -ne 3 ]; then
echo "ERROR: Invalid number of parameters"
usage
fi
###############################################################################
# Setup MQ server working container
###############################################################################
# Use a "scratch" container, so the resulting image has minimal files
# Resulting image won't have yum, for example
readonly basetag=$1
readonly ctr_mq=$(buildah from $basetag)
if [ -z "$ctr_mq" ]
then
echo "ERROR: ctr_mq is empty. Check above output for errors"
exit 50
fi
readonly mnt_mq=$(buildah mount $ctr_mq)
if [ -z "$mnt_mq" ]
then
echo "ERROR: mnt_mq is empty. Check above output for errors"
exit 50
fi
readonly tag=$2
readonly version=$3
useradd --root $mnt_mq --gid mqm admin
groupadd --root $mnt_mq --system mqclient
useradd --root $mnt_mq --gid mqclient app
buildah run $ctr_mq -- id admin
buildah run $ctr_mq -- sh -c "echo admin:passw0rd | chpasswd"
mkdir -p $mnt_mq/run/runmqdevserver
chown 888:888 $mnt_mq/run/runmqdevserver
# Copy runmqdevserver program
install --mode 0750 --owner 888 --group 888 ./build/runmqdevserver ${mnt_mq}/usr/local/bin/
# Copy template files
cp incubating/mqadvanced-server-dev/*.tpl ${mnt_mq}/etc/mqm/
# Copy web XML files for default developer configuration
cp -R incubating/mqadvanced-server-dev/web ${mnt_mq}/etc/mqm/web
###############################################################################
# Final Buildah commands
###############################################################################
buildah config \
--port 1414/tcp \
--port 9157/tcp \
--port 9443/tcp \
--os linux \
--label architecture=x86_64 \
--label io.openshift.tags="mq messaging developer" \
--label io.k8s.display-name="IBM MQ Advanced Server Developer Edition" \
--label io.k8s.description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
--label name="${tag%:*}" \
--label vendor="IBM" \
--label version="$version" \
--label release="1" \
--label run="docker run -d -e LICENSE=accept --name ibm-mq-dev ${tag%:*}" \
--label summary="IBM MQ Advanced Server Developer Edition" \
--label description="IBM MQ is messaging middleware that simplifies and accelerates the integration of diverse applications and business data across multiple platforms. It uses message queues to facilitate the exchanges of information and offers a single messaging solution for cloud, mobile, Internet of Things (IoT) and on-premises environments." \
--label IBM_PRODUCT_ID="98102d16795c4263ad9ca075190a2d4d" \
--label IBM_PRODUCT_NAME="IBM MQ Advanced Server Developer Edition" \
--label IBM_PRODUCT_VERSION="$version" \
--env AMQ_ADDITIONAL_JSON_LOG=1 \
--env LANG=en_US.UTF-8 \
--env LOG_FORMAT=basic \
--env MQ_ADMIN_PASSWORD=passw0rd \
--env MQ_DEV=true \
--entrypoint runmqdevserver \
--user root \
$ctr_mq
buildah unmount $ctr_mq
buildah commit $ctr_mq $tag
buildah rm $ctr_mq

View File

@@ -0,0 +1,106 @@
/*
© Copyright IBM Corporation 2017, 2020
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package containerruntimelogger
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/ibm-messaging/mq-container/internal/containerruntime"
"github.com/ibm-messaging/mq-container/internal/user"
"github.com/ibm-messaging/mq-container/pkg/logger"
)
// LogContainerDetails logs details about the container runtime
func LogContainerDetails(log *logger.Logger) error {
if runtime.GOOS != "linux" {
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
}
log.Printf("CPU architecture: %v", runtime.GOARCH)
kv, err := containerruntime.GetKernelVersion()
if err == nil {
log.Printf("Linux kernel version: %v", kv)
}
cr, err := containerruntime.GetContainerRuntime()
if err == nil {
log.Printf("Container runtime: %v", cr)
}
bi, err := containerruntime.GetBaseImage()
if err == nil {
log.Printf("Base image: %v", bi)
}
u, err := user.GetUser()
if err != nil {
log.Printf("Error: %v\nUser:\n uid: %v\n gid: %v\n supGid: %v", err, u.UID, u.PrimaryGID, u.SupplementalGID)
}
if err == nil {
if len(u.SupplementalGID) == 0 {
log.Printf("Running as user ID %v with primary group %v", u.UID, u.PrimaryGID)
} else {
log.Printf("Running as user ID %v with primary group %v, and supplementary groups %v", u.UID, u.PrimaryGID, strings.Trim(strings.Join(strings.Fields(fmt.Sprint(u.SupplementalGID)), ","), "[]"))
}
}
caps, err := containerruntime.GetCapabilities()
capLogged := false
if err == nil {
for k, v := range caps {
if len(v) > 0 {
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
capLogged = true
}
}
if !capLogged {
log.Print("Capabilities: none")
}
} else {
log.Errorf("Error getting capabilities: %v", err)
}
sc, err := containerruntime.GetSeccomp()
if err == nil {
log.Printf("seccomp enforcing mode: %v", sc)
}
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
m, err := containerruntime.GetMounts()
if err == nil {
if len(m) == 0 {
log.Print("No volume detected. Persistent messages may be lost")
} else {
for mountPoint, fsType := range m {
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
if !containerruntime.SupportedFilesystem(fsType) {
return fmt.Errorf("%v uses unsupported filesystem type: %v", mountPoint, fsType)
}
}
}
}
// For a multi-instance queue manager - check all required mounts exist & validate filesystem type
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
log.Println("Multi-instance queue manager: enabled")
reqMounts := []string{"/mnt/mqm", "/mnt/mqm-log", "/mnt/mqm-data"}
for _, mountPoint := range reqMounts {
if fsType, ok := m[mountPoint]; ok {
if !containerruntime.ValidMultiInstanceFilesystem(fsType) {
return fmt.Errorf("%v uses filesystem type '%v' which is invalid for a multi-instance queue manager", mountPoint, fsType)
}
} else {
return fmt.Errorf("Missing required mount '%v' for a multi-instance queue manager", mountPoint)
}
}
}
return nil
}

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ type Logger struct {
pid string pid string
serverName string serverName string
host string host string
user *user.User userName string
} }
// NewLogger creates a new logger // NewLogger creates a new logger
@@ -53,9 +53,13 @@ func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Log
if err != nil { if err != nil {
return nil, err return nil, err
} }
// This can fail because the container's running as a random UID which
// is not known by the OS. We don't want this to break the logging
// entirely, so just use a blank user name.
user, err := user.Current() user, err := user.Current()
if err != nil { userName := ""
return nil, err if err == nil {
userName = user.Username
} }
return &Logger{ return &Logger{
mutex: sync.Mutex{}, mutex: sync.Mutex{},
@@ -66,7 +70,7 @@ func NewLogger(writer io.Writer, debug bool, json bool, serverName string) (*Log
pid: strconv.Itoa(os.Getpid()), pid: strconv.Itoa(os.Getpid()),
serverName: serverName, serverName: serverName,
host: hostname, host: hostname,
user: user, userName: userName,
}, nil }, nil
} }
@@ -93,7 +97,7 @@ func (l *Logger) log(level string, msg string) {
"ibm_serverName": l.serverName, "ibm_serverName": l.serverName,
"ibm_processName": l.processName, "ibm_processName": l.processName,
"ibm_processId": l.pid, "ibm_processId": l.pid,
"ibm_userName": l.user.Username, "ibm_userName": l.userName,
"type": "mq_containerlog", "type": "mq_containerlog",
} }
s, err := l.format(entry) s, err := l.format(entry)
@@ -110,11 +114,6 @@ func (l *Logger) log(level string, msg string) {
l.mutex.Unlock() l.mutex.Unlock()
} }
// LogDirect logs a message directly to stdout
func (l *Logger) LogDirect(msg string) {
fmt.Println(msg)
}
// Debug logs a line as debug // Debug logs a line as debug
func (l *Logger) Debug(args ...interface{}) { func (l *Logger) Debug(args ...interface{}) {
if l.debug { if l.debug {

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2019
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

Some files were not shown because too many files have changed in this diff Show More